最近因爲在上安卓課,做了一個簡單的安卓天氣查詢APP,正好剛上交該軟件的作業報告,順帶寫一篇博文將其記錄一下。該APP的功能是可以查看目前我國三萬多個地區的當天以及未來三天的天氣狀況,而查詢的輸入是城市ID、城市中文名稱或者城市拼音三者都可以。廢話不多說,開始貼代碼了
1 佈局代碼說明:
1.1 activity_main佈局代碼
這個App自己加入了啓動的動畫顯示,即點擊應用圖標,即顯示靜態圖片1500毫秒之後再進入天氣獲取界面。因此在主佈局
activity_main中只放置一張圖片。將這張照片設置成爲背景即可。代碼如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.apress.gerber.jsonapp.MainActivity"
android:background="@drawable/timg">
1.2acyivity_choose佈局代碼
這個佈局用於顯示查詢城市的天氣,佈局較爲複雜。代碼如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/buju"
android:background="@drawable/mainbackground">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true">
<TextView
android:id="@+id/showcity"
android:layout_width="match_parent"
android:layout_height="74dp"
android:text="城市"
android:textSize="50dp"
android:textColor="#dd413939"
android:gravity="center"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="420dp">
<ImageView
android:id="@+id/weatherpicture"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="52dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:layout_marginTop="44dp" />
<TextView
android:id="@+id/weathercondition"
android:layout_width="140dp"
android:layout_height="40dp"
android:textSize="16dp"
android:layout_below="@+id/weatherpicture"
android:layout_alignStart="@+id/weatherpicture"
android:layout_marginTop="15dp" />
<TextView
android:id="@+id/weathertempter"
android:layout_width="150dp"
android:layout_height="120dp"
android:textSize="16dp"
android:layout_alignTop="@+id/weatherpicture"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@+id/weathercondition" />
<TextView
android:id="@+id/otherday"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="18dp"
android:textSize="16dp"
android:layout_below="@+id/weathercondition"
android:layout_alignParentStart="true" />
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<Button
android:id="@+id/beijing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="2dp"
android:layout_weight="1"
android:text="北京"
android:background="@drawable/button_grey"
/>
<EditText
android:id="@+id/insert1"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:background="#aba8a8"
android:focusable="false"/>
<Button
android:id="@+id/xuzhou"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/beijing"
android:layout_weight="1"
android:text="徐州"
android:background="@drawable/button_grey"/>
<EditText
android:id="@+id/insert2"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:background="#aba8a8"
android:focusable="false"/>
<Button
android:id="@+id/shanghai"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/insert2"
android:layout_weight="1"
android:text="上海"
android:background="@drawable/button_grey"/>
<EditText
android:id="@+id/insert3"
android:layout_width="1dp"
android:layout_height="wrap_content"
android:background="#aba8a8"
android:focusable="false"/>
<Button
android:id="@+id/othercity"
android:layout_width="105dp"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/insert3"
android:text="其他城市...."
android:background="@drawable/button_grey"/>
</LinearLayout>
</RelativeLayout>
總體爲相對佈局,在相對佈局中分爲了三大塊,頂部,中部以及底部。頂部用於顯示用戶查詢的城市的名稱,中部主要是
ImageView和各類TextView控件,ImageView用於顯示當天該城市的天氣情況,而TextView用於顯示返回的Json天氣數據經過
解析之後的結果。底部用於顯示四個按鈕,分別是顯示西安、徐州、深圳以及其他城市。點擊前三個城市可以在頂部和中部實現
顯示西安、徐州、深圳以及其他城市。點擊前三個城市可以在頂部和中部實現顯示該城市的當天以及未來三天的天氣狀況。而點
擊其他按鈕則會提示用戶輸入一個需要查詢的城市,確認之後同樣可以顯示查詢城市的天氣狀況。
2 Activity代碼說明
2.1 MainActivity代碼說明:
MainActivity用於顯示啓動的動畫,因此在onCreate()中只開了一個線程用於延時1.5秒操作,延時完成即跳轉到ChooseActivity
中查看城市天氣。代碼如下所示:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent mainIntent=new Intent (MainActivity.this,ChooseActivity.class);
MainActivity.this.startActivity(mainIntent);
finish();
}
},1500);
}
2.2 ChooseActivity 代碼說明
這個活動中主要實現的就是根據用戶輸入的城市名稱、拼音或者城市ID來查詢當地當天以及未來三天的天氣狀況。
按照程序運行的順序,首先點擊按鈕來獲取天氣:
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.beijing:
City_ID = "101010100";
City_Name = "北京";
City_Provience = "北京";
sendRequestWithHttpClient();
break;
case R.id.xuzhou:
City_ID = "101190801";
City_Name = "徐州";
City_Provience = "江蘇";
sendRequestWithHttpClient();
break;
case R.id.shanghai:
City_ID = "101020100";
City_Name = "上海";
City_Provience = "上海";
sendRequestWithHttpClient();
break;
case R.id.othercity:
create_dialog();
break;
default:
City_Name = "徐州";
sendRequestWithHttpClient();
break;
}
}
onClick()函數來判斷點擊了哪個按鈕,之後將City_Name賦值爲該城市的名稱。若點擊的是前三個城市按鈕,則接下來調用
sendRequestWithHttpClient()進行網絡請求。
public void sendRequestWithHttpClient(){
new Thread(new Runnable() {
@Override
public void run() {
int code;
try {
String path = "http://api.map.baidu.com/telematics/v3/weather?location="+City_Name+"&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC";
URL url = new URL(path);
/**
* 這裏網絡請求使用的是類HttpURLConnection,另外一種可以選擇使用類HttpClient。
*/
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");//使用GET方法獲取
conn.setConnectTimeout(5000);
code = conn.getResponseCode();
if (code == 200) {
/**
* 如果獲取的code爲200,則證明數據獲取是正確的。
*/
InputStream is = conn.getInputStream();
String result = HttpUtils.readMyInputStream(is);
/**
* 子線程發送消息到主線程,並將獲取的結果帶到主線程,讓主線程來更新UI。
*/
Message msg = new Message();
msg.obj = result;
msg.what = SUCCESS;
handler.sendMessage(msg);
}else{
Message msg = new Message();
msg.what = ERRORCODE;
handler.sendMessage(msg);
}
}catch (Exception e){
Toast.makeText(ChooseActivity.this,"網絡請求失敗",Toast.LENGTH_SHORT).show();
}
}
}).start();
}
sendRequestWithHttpClient()函數中,根據輸入的網址信息path,利用網絡請求類HttpURLConnection與服務器建立連接,設置請求方式爲“GET”,連接超時爲5000,之後根據返回碼code是否爲200來判斷獲取數據是否成功。不成功則提示錯誤,成功則獲取字節
流,再用自定義的HttpUtils將獲取的字節流轉換成字符串再通過hangler將其發送到主線程中的Handler中去。
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case SUCCESS:
/**
* 獲取信息成功後,對該信息進行JSON解析,得到所需要的信息,然後在textView上展示出來。
*/
JSONAnalysis(msg.obj.toString());
Toast.makeText(ChooseActivity.this, "獲取數據成功", Toast.LENGTH_SHORT)
.show();
break;
case FAILURE:
Toast.makeText(ChooseActivity.this, "獲取數據失敗", Toast.LENGTH_SHORT)
.show();
break;
case PICTURE:
weatherpiture.setImageDrawable((Drawable) msg.obj);
break;
case ERRORCODE:
Toast.makeText(ChooseActivity.this, "獲取的CODE碼不爲200!",
Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
};
Msg的狀態碼有四個,分別是成功獲取數據,獲取數據失敗,獲取照片(在請求照片中會用到),網絡請求失敗。在成功獲取數據時,調用JSONAnalysis對獲取到的字符串進行Json解析。
在解析之前,先來看看這個網址:
http://api.map.baidu.com/telematics/v3/weather?location=徐州&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC
只要把徐州換成任何一個地區就行,接着看看它返回的數據:
{"error":0,"status":"success","date":"2017-06-30","results":[{"currentCity":"徐州","pm25":"105","index":[{"title":"穿衣","zs":"炎熱","tipt":"穿衣指數","des":"
天氣炎熱,建議着短衫、短裙、短褲、薄型T恤衫等清涼夏季服裝。"},{"title":"洗車","zs":"不宜","tipt":"洗車指數","des":"不宜洗車,未來24小時內有雨,
如果在此期間洗車,雨水和路上的泥水可能會再次弄髒您的愛車。"},{"title":"感冒","zs":"少發","tipt":"感冒指數","des":"各項氣象條件適宜,發生感冒機率
較低。但請避免長期處於空調房間中,以防感冒。"},{"title":"運動","zs":"較不宜","tipt":"運動指數","des":"有降水,推薦您在室內進行低強度運動;若堅
持戶外運動,須注意選擇避雨防滑並攜帶雨具。"},{"title":"紫外線強度","zs":"弱","tipt":"紫外線強度指數","des":"紫外線強度較弱,建議出門前塗擦SPF
在12-15之間、PA+的防曬護膚品。"}],"weather_data":[{"date":"週五 06月30日 (實時:31℃)","dayPictureUrl":"http://api.map.baidu.com/images/
weather/day/leizhenyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/zhongyu.png","weather":"雷陣雨轉中雨","wind":"南風
微風","temperature":"33 ~ 23℃"},{"date":"週六","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/zhongyu.png","nightPictureUrl":"http:
//api.map.baidu.com/images/weather/night/yin.png","weather":"中雨轉陰","wind":"南風微風","temperature":"29 ~ 23℃"},{"date":"週日","dayPictureUrl"
:"http://api.map.baidu.com/images/weather/day/xiaoyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/yin.png","weather":"小
雨轉陰","wind":"西南風微風","temperature":"32 ~ 24℃"},{"date":"週一","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/xiaoyu.png",
"nightPictureUrl":"http://api.map.baidu.com/images/weather/night/xiaoyu.png","weather":"小雨","wind":"南風微風","temperature":"31 ~ 23℃"}]}]}
以上就是服務器返回的JSON數據,接下來就是解析就可以了:
protected void JSONAnalysis(String string) {
JSONObject o;
try {
o = new JSONObject(string);
int returnResult = o.getInt("error");
if (returnResult == 0) { //returnResult表示成功返回JSON數據
String date = o.getString("date"); //日期
JSONArray posts = o.getJSONArray(("results"));// 只有一個result
JSONObject tempObj = (JSONObject) posts.get(0);// 第0個對象,第0個對象是當天的天氣,先只顯示當天的天氣
cityname.setText(tempObj.getString("currentCity")); //設置城市的名字
JSONArray tempArr = tempObj.getJSONArray("weather_data");
JSONObject tempWeatherData = (JSONObject) tempArr.get(0);// 當天天氣
//時間、溫度以及PM2.5
String PM = tempObj.getString("pm25");
if (PM.length()==0)PM="無數據";
temptureanddate.setText("時間:"+tempWeatherData.getString("date")+"\n"+tempWeatherData.getString("temperature")+"\n"+"PM2.5:"+PM);
//獲取天氣圖片
image_url = tempWeatherData.getString("dayPictureUrl").toString();
//獲取天氣狀況圖片
getBitmap(image_url);
weatherpiture.setImageDrawable(drawable);
//天氣狀況以及風力
weatherandwind.setText(tempWeatherData.getString("weather")+" "+tempWeatherData.getString("wind"));
/*獲取後三天的數據
* */
String threedays = null;
JSONObject day1 = (JSONObject) tempArr.get(1);// 第1個對象,第1對象是後一天的數據
JSONObject day2 = (JSONObject) tempArr.get(2);//第2個對象,第2對象是後兩天的數據
JSONObject day3 = (JSONObject) tempArr.get(3);//第3個對象,第3對象是後三天的數據
threedays = "\n"+tempObj.getString("currentCity")+"接下來三天的天氣狀況"+"\n"+"\n"+day1.getString("date")+":"+day1.getString("weather")+"
溫度:"+day1.getString("temperature")+" 風力:"+day1.getString("wind")+"\n"+"\n"+
day2.getString("date")+":"+day2.getString("weather")+" 溫度:"+day2.getString("temperature")+" 風力:"+day2.getString("wind")+"\n"+"\n"+
day3.getString("date")+":"+day3.getString("weather")+" 溫度:"+day3.getString("temperature")+" 風力:"+day3.getString("wind");
otherdays.setText(threedays);
}
} catch (JSONException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
/**
* 在獲取的string這個JSON對象中,提取所需要的信息。先測試一下
*/
System.out.println(string);
Log.i("JSON",string);
}
上述即爲Json解析的函數,解析過程比較簡單,解析之前先創建一個JSONObject,之後根據key值來獲取數據,其中會涉及到JSON數組,需要用到
JSONArry,但是總體解析並不複雜,之後將解析的數據顯示在TextView中即可,這樣可以顯示該地區當天以及未來三天的天氣狀況。
但其中有個問題,顯示當天天氣的圖片返回的是一個網址,因此還需要通過網絡編程來獲取圖片。代碼如下所示:
public void getBitmap(final String path) throws IOException {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection conn = null;
conn = (HttpURLConnection)url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode() == 200){
InputStream inputStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
drawable = new BitmapDrawable(bitmap);
Log.i("Bitmap",bitmap.toString());
Log.i("Drawable",drawable.toString());
Message msg = new Message();
msg.obj = drawable;
msg.what = PICTURE;
handler.sendMessage(msg);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
GetBitmap()獲取照片的方法和之前網絡請求JSON數據思路一模一樣,請求完之後,將msg的狀態碼置爲 PICTURE(獲取照片),調用Handle在主線程中更新
ImageView即可。至此即完成了天氣的獲取。
接下來是第四個按鈕,點擊之後生成的是一個可以輸入文字的AlertDialog提示框,點擊之後用戶可以用三種方式輸入:城市的中文名稱、城市的拼音、城市的ID,
輸入其中任何一項都可以查詢到當地的天氣。代碼如下所示:
public void create_dialog(){
final EditText et = new EditText(this);
et.setMaxLines(1);
AlertDialog.Builder builder = new AlertDialog.Builder(ChooseActivity.this);
builder.setTitle("查詢的城市名稱,格式例如:徐州 OR (xuzhou),拼音不區分大小寫");
builder.setIcon(android.R.drawable.btn_star);
builder.setView(et);
builder.setPositiveButton("確認", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
City_Name = et.getText().toString();
if (City_Name == null) {
Toast.makeText(ChooseActivity.this, "請不要輸入錯誤的數據", Toast.LENGTH_SHORT).show();
}
if (City_Name.length()==0)
City_Name="徐州";
sendRequestWithHttpClient();
}
});
builder.setNegativeButton("取消",null);
builder.show();
}
其中調用的請求JSON以及解析JSON數據的函數在之前已經說明。這樣就可以實現對我國目前三萬多個不同地區天氣的查詢,功能非常強大。最後的一部分關鍵代碼是自己寫的將網絡請求得到的字節流轉換成字符串的HttpUtils類。代碼如下:
public class HttpUtils {
public static String readMyInputStream(InputStream is) {
byte[] result;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer))!=-1) {
baos.write(buffer,0,len);
}
is.close();
baos.close();
result = baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
String errorStr = "獲取數據失敗。";
return errorStr;
}
return new String(result);
}
(最後需要註明一項,目前百度天氣查詢不再提供天氣查詢API的獲取,此次的鏈接:http://api.map.baidu.com/telematics/v3/weather?location=徐州&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC 來自CSDN博主:u012606241,網址鏈接:http://blog.csdn.net/u012606241/article/details/25656207)
二、顯示截圖
最後該項目的資源鏈接如下:
http://download.csdn.net/detail/jian_yun_rui/9889477