極簡天氣app
最近在學習Android,這裡就用這個極簡天氣app練練手吧。
製作思路:
- UI佈局
- 城市選擇
- 對應城市天氣資料獲取與處理
- 重新整理UI
先看看效果吧(前面有點遲鈍):
我點選右上角的城市管理按鈕後進入城市選擇(裡面的城市是隨便寫的哦),確認後返回主介面,這裡只顯示高溫而已。
確實極簡吧,但是其中的處理過程可是一點沒有少的!是要少加布局上的修飾就瞬間高大上了。
先看佈局檔案(activity_main.xml):
@+id/activity_main_textview_city這是用於顯示我們選擇的城市的TextView。
@+id/activity_main_textview_content這是顯示天氣內容的TextView(我偷懶把所有的內容都放進去了,嘿嘿,嘿嘿)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/activity_main_button_chengshiguanli" android:layout_width="50dp" android:layout_height="25dp" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:layout_marginTop="10dp" android:layout_marginRight="10dp" android:text="@string/chengshiguanli" android:background="@color/colorWhite" android:textSize="5pt"/> <TextView android:id="@+id/activity_main_textview_city" android:layout_width="150dp" android:layout_height="50dp" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:textSize="17pt" android:gravity="center" /> <TextView android:id="@+id/activity_main_textview_content" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/activity_main_textview_city" android:layout_marginTop="10dp" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_marginBottom="10dp" /> </RelativeLayout>
下面是選擇城市的佈局檔案(choose_city.xml):
其中我們用到了com.wx.wheelview.widget.WheelView元件,這是一個開源的滾輪元件,使用之前需要先匯入相應的包,這裡是其專案地址和詳細的使用說明:https://github.com/venshine/WheelView
"@+id/city_btn",android:text="boom" 這個按鈕是我們做測試用的。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.wx.wheelview.widget.WheelView android:id="@+id/wheel_province" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> <com.wx.wheelview.widget.WheelView android:id="@+id/wheel_city" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> <com.wx.wheelview.widget.WheelView android:id="@+id/wheel_area" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"/> </LinearLayout> <Button android:id="@+id/city_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="boom" android:layout_gravity="center"/> <Button android:id="@+id/makesure_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="確認" android:layout_gravity="center"/> </LinearLayout>
下面是我們的MainActivity.java檔案:
負責為按鈕的監聽和重新整理UI。
這裡我們使用startActivityForResult(intent, requestcode)方法進行UI頁面的跳轉。並用onActivityResult(int requestCode, int resultCode, Intent data)方法根據對應的requestcode來接受ChooseCity.java檔案處理後含天氣資料的bundle物件,進而重新整理UI。
package com.example.administrator.myapplication_weather;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button btn_glcs;
private TextView text_city;
private TextView text_content;
private int requestcode;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
btn_glcs = (Button) findViewById(R.id.activity_main_button_chengshiguanli);
btn_glcs.setOnClickListener(new btn_glcsListener());
text_city = (TextView) findViewById(R.id.activity_main_textview_city);
text_content = (TextView)findViewById(R.id.activity_main_textview_content);
}
class btn_glcsListener implements View.OnClickListener {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, ChooseCity.class);
requestcode = 0;
startActivityForResult(intent, requestcode);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 0) {
Bundle bundle =data.getExtras();
text_city.setText(bundle.getString("city"));
text_content.setText(bundle.getString("high"));
}
}
}
下面是ChooseCity.java檔案用來處理choosecity.xml佈局檔案對應的邏輯,其中對滾輪的配置較多,但是基本流程是一樣的。
在確認按鈕的監聽事件中有這麼一句: new GetContacts().execute();可以在下面看到這個類是繼承AsyncTask類的。這是android提供的非同步類。(因為android不允許將耗時的操作放在UI執行緒裡,所以要使用非同步類(當然新開執行緒也可以),即
Void doInBackground(Void... voids)來在後臺下載我們的天氣資料。
package com.example.administrator.myapplication_weather;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.wx.wheelview.adapter.ArrayWheelAdapter;
import com.wx.wheelview.widget.WheelView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
public class ChooseCity extends Activity {
public static final String action = "jason.broadcast.action";
private int requestcode;
private WheelView wheel_province;
private WheelView wheel_city;
private WheelView wheel_area;
private Button btn_city;
private Button btn_makesure;
private Bundle bundle = new Bundle();
private static String cityurl = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.choose_city);
initCity();
}
private void initCity()
{
wheel_province = findViewById(R.id.wheel_province);
wheel_city = findViewById(R.id.wheel_city);
wheel_area = findViewById(R.id.wheel_area);
//定義WheelView的style,比如選中文字大小和其他文字大小(這裡WheelView已經封裝了)
WheelView.WheelViewStyle style = new WheelView.WheelViewStyle();
style.selectedTextSize = 18;
style.textSize = 14;
//在這裡設定一個WheelView的Adapter作為資料來源的介面卡
wheel_province.setWheelAdapter(new ArrayWheelAdapter(this));
//為WheelView設定一個面板風格(這裡在WheelView中已經封裝了一個Holo)
wheel_province.setSkin(WheelView.Skin.Holo);
//這裡將資料放入WheelView中
wheel_province.setWheelData(createProvinceDatas());
//設定WheelView的Style(上面已經定義)
wheel_province.setStyle(style);
wheel_city.setWheelAdapter(new ArrayWheelAdapter(this));
wheel_city.setSkin(WheelView.Skin.Holo);
//這裡就很奇妙了,我詳細說一下
//看下面的幾個建立資料的函式,從province到city再到area,其中的返回型別中分別為List<String>,HashMap<String, List<String>>, HashMap<String, List<String>>
//其中第一種為String列表,也就是第一個省份的列表可以直接通過String列表得到。
//HashMap是雜湊表,他裡面的值都是通過key-value進行對應,所以在這個情況中就是一個省(String key)對應著一個市(String value)的列表(同理得到第二個市與區的關係)
//HashMap.get(key)方法是用來通過key的值來得到value的值
//WheelView.getSelection()通過看就知道是一個獲取位置的方法(大神在WheelView中封裝好了).
//綜上所述,其實這條東西,逆向來讀就是,通過得到省的WheelView的位置來得到省的value值,而省的value值就是市的key值,所以說可以得到市的一整個value值。
wheel_city.setWheelData(createCityDatas().get(createProvinceDatas().get(wheel_province.getSelection())));
wheel_city.setStyle(style);
//這裡是把省的WheelView與市的WheelView連線起來(封裝好的)(加入下一級的WheelView)
wheel_province.join(wheel_city);
//這裡是把省的WheelView與市的WheelView的資料連線起來
wheel_province.joinDatas(createCityDatas());
wheel_area.setWheelAdapter(new ArrayWheelAdapter(this));
wheel_area.setSkin(WheelView.Skin.Holo);
//這個嘛,上面解釋過了,但是又臭又長,簡單說一下
//其實就匹配了兩次,通過得到省和市的位置來定位到他們兩個的value,再通過value得到區的value值
wheel_area.setWheelData(createAreaDatas().get(createCityDatas().get(createProvinceDatas().get(wheel_province.getSelection())).get(wheel_city.getSelection())));
wheel_area.setStyle(style);
wheel_city.join(wheel_area);
wheel_city.joinDatas(createAreaDatas());
btn_city = (Button)findViewById(R.id.city_btn);
btn_city.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String province = wheel_province.getSelectionItem().toString();
String city = wheel_city.getSelectionItem().toString();
String area = wheel_area.getSelectionItem().toString();
Toast.makeText(ChooseCity.this, province + city + area, Toast.LENGTH_SHORT).show();
}
});
btn_makesure = (Button)findViewById(R.id.makesure_btn);
btn_makesure.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String area = wheel_province.getSelectionItem().toString();
cityurl = "http://wthrcdn.etouch.cn/weather_mini?city="+area;
Log.d("DATA",cityurl);
new GetContacts().execute();
}
});
}
//這裡是第一級,所以直接把他放入一個List中就可以了
private List<String> createProvinceDatas() {
String[] strings = {"黑龍江", "吉林", "北京"};
//將字元陣列轉換為List形式
return Arrays.asList(strings);
}
private HashMap<String, List<String>> createCityDatas() {
//新建一個雜湊表
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
String[] strings = {"黑龍江", "吉林","北京"};
String[] s1 = {"哈爾濱", "齊齊哈爾", "大慶"};
String[] s2 = {"長春", "吉林"};
String[] s3 = {"瀋陽", "大連", "鞍山", "撫順"};
String[][] ss = {s1, s2, s3};
for (int i = 0; i < strings.length; i++) {
//在這裡把key與value分別列出,然後通過HashMap.put進行配對然後寫入雜湊表。
map.put(strings[i], Arrays.asList(ss[i]));
}
// 一個雜湊表的輸出檢測(自學雜湊表時測試一下用的,自己也可以試試)
// Iterator iter = map.entrySet().iterator();
// while(iter.hasNext()) {
// Map.Entry entry = (Map.Entry) iter.next();
// Object key = entry.getKey();
// Object value = entry.getValue();
// Log.v("second",key + ":" + value);
// }
return map;
}
private HashMap<String, List<String>> createAreaDatas() {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
String[] strings = {"哈爾濱", "齊齊哈爾", "大慶", "長春", "吉林", "瀋陽", "大連", "鞍山", "撫順"};
String[] s1 = {"道里區", "道外區", "南崗區", "香坊區"};
String[] s2 = {"龍沙區", "建華區", "鐵鋒區"};
String[] s3 = {"紅崗區", "大同區"};
String[] s11 = {"南關區", "朝陽區"};
String[] s12 = {"龍潭區"};
String[] s21 = {"和平區", "皇姑區", "大東區", "鐵西區"};
String[] s22 = {"中山區", "金州區"};
String[] s23 = {"鐵東區", "鐵西區"};
String[] s24 = {"新撫區", "望花區", "順城區"};
String[][] ss = {s1, s2, s3, s11, s12, s21, s22, s23, s24};
for (int i = 0; i < strings.length; i++) {
map.put(strings[i], Arrays.asList(ss[i]));
}
// Iterator iter = map.entrySet().iterator();
// while(iter.hasNext()) {
// Map.Entry entry = (Map.Entry) iter.next();
// Object key = entry.getKey();
// Object value = entry.getValue();
// Log.v("first",key + ":" + value);
// }
return map;
}
public Bundle jsonParse(String cityjsondata) throws JSONException {
Bundle jsonbundle = new Bundle();
JSONObject reader = new JSONObject(cityjsondata);
JSONObject weatherdata = reader.getJSONObject("data");
JSONArray forecast = weatherdata.getJSONArray("forecast");
JSONObject todayweather = (JSONObject) forecast.get(0);
Log.d("DATA",todayweather.getString("low"));
Log.d("DATA",todayweather.getString("fengli"));
Log.d("DATA",todayweather.getString("fengxiang"));
Log.d("DATA",todayweather.getString("type"));
jsonbundle.putString("high",todayweather.getString("high"));
jsonbundle.putString("low",todayweather.getString("low"));
jsonbundle.putString("fengli",todayweather.getString("fengli"));
jsonbundle.putString("fengxiang",todayweather.getString("fengxiang"));
jsonbundle.putString("type",todayweather.getString("type"));
return jsonbundle;
}
private class GetContacts extends AsyncTask<Void, Void, Void>
{
@Override
protected void onPreExecute() {
super.onPreExecute();
Log.d("DATA","Json Data is downloading");
Toast.makeText(ChooseCity.this,"Json Data is downloading",Toast.LENGTH_LONG).show();
}
@Override
protected Void doInBackground(Void... voids) {
Log.d("DATA","Deal with data");
GetCityJsonData getdata = new GetCityJsonData();
String jsoncontent = getdata.GetDataContent(cityurl);
try {
bundle = jsonParse(jsoncontent);
} catch (JSONException e) {
Log.d("DATA","ERROR in doInBackground");
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
String area = wheel_province.getSelectionItem().toString();
bundle.putString("city",area);
bundle.putString("joke","haha");
Log.d("DATA","POST!");
Log.d("DATA",bundle.getString("high"));
Log.d("DATA","POST2");
Intent intent = new Intent(ChooseCity.this,MainActivity.class);
intent.putExtras(bundle);
requestcode = 0;
setResult(requestcode,intent);
finish();
}
}
}
GetDataContent.java檔案:用來根據URL來獲取對應的Json檔案,並以String的格式返回
:
package com.example.administrator.myapplication_weather;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class GetCityJsonData {
private String url="";
public GetCityJsonData()
{
}
public String GetDataContent(String url)
{
String response = null;
setUrl(url);
Log.d("DATA","download the data...");
try {
URL Url = new URL(getUrl());
HttpURLConnection conn = (HttpURLConnection) Url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(8000);
conn.setReadTimeout(8000);
InputStream in = new BufferedInputStream(conn.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuffer sb = new StringBuffer();
String str;
while((str = reader.readLine())!=null)
{
sb.append(str);
}
response = sb.toString();
Log.d("GetData",response);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
private String getUrl()
{
return url;
}
private void setUrl(String url)
{
this.url = url;
}
}
對應的配置檔案:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.administrator.myapplication_weather">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ChooseCity"
android:exported="true"/>
</application>
</manifest>
到此我的極簡天氣App就結束了。
雖然簡陋,但是我們學習了以下內容:
- Android單執行緒模型
- 兩個Activity之間傳遞資料
- 非同步類AsyncTask
- 資料結構Map
- Json格式解析
- 多級聯動的滾輪