Android天氣預(yù)報(bào)app改進(jìn)版
最近總是有人來(lái)和我說(shuō)我以前寫(xiě)的一個(gè)小app無(wú)法正常獲取數(shù)據(jù)~Android簡(jiǎn)易版天氣預(yù)報(bào)app
今天就又運(yùn)行了下來(lái)查找問(wèn)題,發(fā)現(xiàn)或許是接口有限制吧,不能在多臺(tái)手機(jī)使用同個(gè)apikey
然后,發(fā)現(xiàn)了我寫(xiě)的代碼實(shí)在亂七八糟,界面也實(shí)在不好看,就又重寫(xiě)了一遍,小小地修改了一遍,開(kāi)發(fā)環(huán)境改為了Android Studio
最終效果圖如下
工程圖如下

一、獲取地區(qū)信息
做這么一個(gè)天氣預(yù)報(bào)app,首先就要獲取到國(guó)內(nèi)地區(qū)列表
(在我的另一篇博客有介紹:向任意網(wǎng)址發(fā)起數(shù)據(jù)請(qǐng)求)
中國(guó)天氣網(wǎng)開(kāi)放有天氣預(yù)報(bào)接口,訪問(wèn)“http://www.weather.com.cn/data/list3/city.xml”就可以獲取到國(guó)內(nèi)省份列表以及其代號(hào)了

如果想要獲取廣東省下的城市列表,由上圖可知廣東省的代號(hào)為28,則接口地址是 “http://www.weather.com.cn/data/list3/city28.xml”,獲取到的城市列表及代號(hào)如下:
依次類推還可以獲取到更加詳細(xì)的地區(qū)信息,這樣就完成了開(kāi)頭部分
二、天氣信息的獲取
百度的APIStore擁有豐富的接口,涵蓋了生活的許多方面。例如,我們就可以通過(guò)APIStore的某個(gè)接口獲取到含有天氣信息的JSON數(shù)據(jù),從而實(shí)現(xiàn)天氣預(yù)報(bào)功能
(在我的另一篇博客有介紹:獲取含天氣信息的JSON數(shù)據(jù))
首先,使用者要有一個(gè)百度賬號(hào),然后登陸以下網(wǎng)址:中國(guó)和世界天氣預(yù)報(bào)
該接口是免費(fèi)的,不過(guò)因此也就不夠穩(wěn)定,我在調(diào)試的時(shí)候就經(jīng)常出錯(cuò)
然后在API選項(xiàng)下點(diǎn)擊“您自己的apikey”,查看自己的apikey。該值是每個(gè)開(kāi)發(fā)者和app的唯一標(biāo)識(shí),需要妥善保管,有了apikey才可以進(jìn)行下一步的操作

獲取到的天氣信息是JSON格式的,需要在程序中再來(lái)解析

三、數(shù)據(jù)庫(kù)的設(shè)計(jì)
地區(qū)列表這些信息一般都是固定不變的,所以我們可以把第一次聯(lián)網(wǎng)獲取到的數(shù)據(jù)存進(jìn)數(shù)據(jù)庫(kù)里,下次再次訪問(wèn)時(shí)就從數(shù)據(jù)庫(kù)里讀取即可
首先要設(shè)定四個(gè)Model,包括:省份、城市、縣、每小時(shí)天氣預(yù)測(cè),用來(lái)承載數(shù)據(jù)
每個(gè)Model包括幾個(gè)屬性以及相應(yīng)的get和set方法
例如,省份Province的設(shè)計(jì)如下所示,城市City和縣County的設(shè)計(jì)類似
/**
* 省份
*/
public class Province {
//省份名
private String provinceName;
//省份ID
private String provinceId;
public String getProvinceId() {
return provinceId;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceId(String provinceId) {
this.provinceId = provinceId;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
}
每小時(shí)天氣預(yù)測(cè)HourlyWeather的設(shè)計(jì)如下:
/**
* Created by ZY on 2016/7/21.
*/
public class HourlyWeather {
//預(yù)測(cè)時(shí)間
private String time;
//溫度
private String temp;
//降水概率
private String pop;
//風(fēng)力
private String wind;
public HourlyWeather(String time, String temp, String pop, String wind) {
this.time = time;
this.temp = temp;
this.pop = pop;
this.wind = wind;
}
public String getTime() {
return time;
}
public String getTemp() {
return temp;
}
public String getPop() {
return pop;
}
public String getWind() {
return wind;
}
}
然后,新建一個(gè)DatabaseHelper類繼承于SQLiteOpenHelper,用來(lái)建立三個(gè)數(shù)據(jù)庫(kù)表
public class DatabaseHelper extends SQLiteOpenHelper {
private final String CREATE_PROVINCE = "create table Province ("
+ "provinceName text," + "provinceId text )";
private final String CREATE_CITY = "create table City("
+ "cityName text," + "cityId text," + "provinceId text)";
private final String CREATE_COUNTY = "create table County("
+ "countyName text," + "countyId text," + "cityId text)";
public DatabaseHelper(Context context, String DbName,
CursorFactory factory, int version) {
super(context, DbName, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_PROVINCE);
db.execSQL(CREATE_CITY);
db.execSQL(CREATE_COUNTY);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
然后,再建立一個(gè)WeatherDB類,用來(lái)進(jìn)行實(shí)際的數(shù)據(jù)庫(kù)操作,包括存取省份信息、城市信息、縣信息等
需要注意的是,因?yàn)槊總€(gè)城市都是包含在某個(gè)省份下的,所以查詢某個(gè)省份下的所有城市列表,需要將省份的ID傳入作為唯一標(biāo)識(shí)
public class WeatherDB {
private final String DataBaseName = "ZyWeather";
private final int VERSION = 1;
private SQLiteDatabase database;
private static WeatherDB weatherDB;
private WeatherDB(Context context) {
DatabaseHelper dataBaseHelper = new DatabaseHelper(context,
DataBaseName, null, VERSION);
database = dataBaseHelper.getWritableDatabase();
}
//獲取實(shí)例
public static WeatherDB getInstance(Context context) {
if (weatherDB == null) {
weatherDB = new WeatherDB(context);
}
return weatherDB;
}
//保存省份信息
public void saveProvinces(List<Province> provinceList) {
if (provinceList != null && provinceList.size() > 0) {
ContentValues values = new ContentValues();
for (int i = 0; i < provinceList.size(); i++) {
values.put("provinceName", provinceList.get(i).getProvinceName());
values.put("provinceId", provinceList.get(i).getProvinceId());
database.insert("Province", null, values);
values.clear();
}
}
}
//保存城市信息
public void saveCities(List<City> cityList) {
if (cityList != null && cityList.size() > 0) {
ContentValues values = new ContentValues();
for (int i = 0; i < cityList.size(); i++) {
values.put("cityName", cityList.get(i).getCityName());
values.put("cityId", cityList.get(i).getCityId());
values.put("provinceId", cityList.get(i).getProvinceId());
database.insert("City", null, values);
values.clear();
}
}
}
//保存鄉(xiāng)村信息
public void saveCounties(List<County> countyList) {
if (countyList != null && countyList.size() > 0) {
ContentValues values = new ContentValues();
for (int i = 0; i < countyList.size(); i++) {
values.put("countyName", countyList.get(i).getCountyName());
values.put("countyId", countyList.get(i).getCountyId());
values.put("cityId", countyList.get(i).getCityId());
database.insert("County", null, values);
values.clear();
}
}
}
//返回所有省份信息
public List<Province> getAllProvince() {
Cursor cursor = database.query("Province", null, null, null, null, null, null);
List<Province> list = new ArrayList<>();
Province province;
if (cursor.moveToFirst()) {
do {
province = new Province();
province.setProvinceName(cursor.getString(cursor.getColumnIndex("provinceName")));
province.setProvinceId(cursor.getString(cursor.getColumnIndex("provinceId")));
list.add(province);
} while (cursor.moveToNext());
}
return list;
}
//返回指定省份下的所有城市
public List<City> getAllCity(String provinceId) {
List<City> list = new ArrayList<>();
City city;
Cursor cursor = database.query("City", null, "provinceId = ?", new String[]{provinceId}, null, null, null);
if (cursor.moveToFirst()) {
do {
city = new City();
city.setCityName(cursor.getString(cursor.getColumnIndex("cityName")));
city.setCityId(cursor.getString(cursor.getColumnIndex("cityId")));
city.setProvinceId(provinceId);
list.add(city);
} while (cursor.moveToNext());
}
return list;
}
//返回指定城市下的所有鄉(xiāng)村
public List<County> getAllCountry(String cityId) {
List<County> list = new ArrayList<>();
Cursor cursor = database.query("County", null, "cityId=?", new String[]{cityId}, null, null, null);
County county;
if (cursor.moveToFirst()) {
do {
county = new County();
county.setCountyName(cursor.getString(cursor.getColumnIndex("countyName")));
county.setCountyId(cursor.getString(cursor.getColumnIndex("countyId")));
county.setCityId(cityId);
list.add(county);
} while (cursor.moveToNext());
}
return list;
}
}
四、聯(lián)網(wǎng)操作
整個(gè)app用同一個(gè)函數(shù)來(lái)完成各種數(shù)據(jù)數(shù)據(jù)操作,該函數(shù)包含在HttpUtil類下,為靜態(tài)函數(shù)
當(dāng)中需要填入自己申請(qǐng)的apikey,該key僅在獲取天氣信息時(shí)有用,在獲取地區(qū)信息時(shí)是不需要的,這里只是為了簡(jiǎn)便,所以就一起寫(xiě)上了
public class HttpUtil {
public static void sendHttpRequest(final String address, final HttpCallbackListener listener) {
new Thread(new Runnable() {
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setRequestProperty("apikey", "填入自己的apikey");
connection.connect();
InputStream inputStream = connection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuilder response = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
response.append(line);
}
if (listener != null) {
listener.onFinish(response.toString());
}
} catch (Exception e) {
if (listener != null) {
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
五、工具類
在聯(lián)網(wǎng)訪問(wèn)數(shù)據(jù)成功或失敗后,都需要通過(guò)回調(diào)方法進(jìn)行數(shù)據(jù)處理,所以需要設(shè)定一個(gè)接口HttpCallbackListener
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
此外,使用HttpUtil 類獲取到地區(qū)信息后,因?yàn)閿?shù)據(jù)包含一些分隔符,無(wú)法直接存入數(shù)據(jù)庫(kù),而且獲取到的天氣信息也是JSON格式的,也需要進(jìn)行數(shù)據(jù)解析,所以還需要有一個(gè)Utility類用來(lái)進(jìn)行數(shù)據(jù)處理
public class Utility {
// 保存服務(wù)器返回的省級(jí)數(shù)據(jù)
public static boolean saveProvincesResponse(WeatherDB weatherDB, String response) {
if (!TextUtils.isEmpty(response)) {
String[] allProvinces = response.split(",");
if (allProvinces != null && allProvinces.length > 0) {
Province province;
List<Province> provinceList = new ArrayList<>();
for (String p : allProvinces) {
String[] array = p.split("\\|");
province = new Province();
province.setProvinceId(array[0]);
province.setProvinceName(array[1]);
provinceList.add(province);
}
weatherDB.saveProvinces(provinceList);
return true;
}
}
return false;
}
// 保存服務(wù)器返回的市級(jí)數(shù)據(jù)
public static boolean saveCitiesResponse(WeatherDB weatherDB, String response, String provinceId) {
if (!TextUtils.isEmpty(response)) {
String[] allCities = response.split(",");
if (allCities != null && allCities.length > 0) {
City city;
List<City> cityList = new ArrayList<>();
for (String c : allCities) {
String[] array = c.split("\\|");
city = new City();
city.setCityId(array[0]);
city.setCityName(array[1]);
city.setProvinceId(provinceId);
cityList.add(city);
}
weatherDB.saveCities(cityList);
return true;
}
}
return false;
}
// 保存服務(wù)器返回的縣級(jí)數(shù)據(jù)
public static boolean saveCountiesResponse(WeatherDB weatherDB, String response, String cityId) {
if (!TextUtils.isEmpty(response)) {
String[] allCounties = response.split(",");
if (allCounties != null && allCounties.length > 0) {
County county;
List<County> countyList = new ArrayList<>();
for (String c : allCounties) {
String[] array = c.split("\\|");
county = new County();
county.setCountyId(array[0]);
county.setCountyName(array[1]);
county.setCityId(cityId);
countyList.add(county);
}
weatherDB.saveCounties(countyList);
return true;
}
}
return false;
}
// 處理服務(wù)器返回的json數(shù)據(jù)
public static void handleWeatherResponse(Context context, String response) {
try {
JSONObject jsonobject = new JSONObject(response);
JSONArray title = jsonobject.getJSONArray("HeWeather data service 3.0");
JSONObject first_object = (JSONObject) title.get(0);
JSONObject basic = (JSONObject) first_object.get("basic");
//更新時(shí)間
JSONObject update = (JSONObject) basic.get("update");
JSONArray daily_forecast = (JSONArray) first_object.get("daily_forecast");
JSONObject daily_forecast_first = (JSONObject) daily_forecast.get(0);
JSONObject cond = (JSONObject) daily_forecast_first.get("cond");
//溫度
JSONObject temp = (JSONObject) daily_forecast_first.get("tmp");
JSONObject astro = (JSONObject) daily_forecast_first.get("astro");
JSONObject wind = (JSONObject) daily_forecast_first.get("wind");
JSONArray hourly_forecast = (JSONArray) first_object.get("hourly_forecast");
WeatherActivity.weatherList.clear();
for (int i = 0; i < hourly_forecast.length(); i++) {
JSONObject json = hourly_forecast.getJSONObject(i);
JSONObject json_wind = (JSONObject) json.get("wind");
String date = json.getString("date");
String[] array = date.split(" ");
String dir = json_wind.getString("dir");
String sc = json_wind.getString("sc");
String hourly_clock = array[1];
String hourly_temp = "溫度:" + json.getString("tmp") + "℃";
String hourly_pop = "降水概率:" + json.getString("pop");
String hourly_wind = "風(fēng)力:" + dir + " " + sc + "級(jí)";
HourlyWeather weather = new HourlyWeather(hourly_clock, hourly_temp, hourly_pop, hourly_wind);
WeatherActivity.weatherList.add(weather);
}
//日出
String sunriseTime = astro.getString("sr");
//日落
String sunsetTime = astro.getString("ss");
//白天天氣
String dayWeather = cond.getString("txt_d");
//夜晚天氣
String nightWeather = cond.getString("txt_n");
//風(fēng)力
String windText = wind.getString("dir") + " " + wind.getString("sc") + "級(jí)";
//降水概率
String pop = daily_forecast_first.getString("pop");
//溫度
String tempText = temp.getString("min") + "℃~" + temp.getString("max") + "℃";
//更新時(shí)間
String updateTime = update.getString("loc");
//城市名
String cityName = basic.getString("city");
saveWeatherInfo(context, cityName, sunriseTime, sunsetTime, dayWeather, nightWeather, windText, pop, tempText, updateTime);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void saveWeatherInfo(Context context, String cityName,
String sunriseTime, String sunsetTime, String dayWeather, String nightWeather,
String windText, String pop, String tempText, String updateTime) {
SharedPreferences.Editor editor = context.getSharedPreferences("Weather", Context.MODE_PRIVATE).edit();
editor.putString("cityName", cityName);
editor.putString("sunriseTime", sunriseTime);
editor.putString("sunsetTime", sunsetTime);
editor.putString("dayWeather", dayWeather);
editor.putString("nightWeather", nightWeather);
editor.putString("wind", windText);
editor.putString("pop", pop);
editor.putString("temp", tempText);
editor.putString("updateTime", updateTime);
editor.commit();
}
}
六、適配器
由上邊的動(dòng)態(tài)圖可以看到每小時(shí)的天氣預(yù)測(cè)信息,那是使用ListView呈現(xiàn)的,這就要為其提供一個(gè)適配器了
ListView使用的布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 時(shí)間 -->
<TextView
android:id="@+id/forecastTime"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
android:gravity="center"
android:textSize="20sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="5"
android:orientation="vertical">
<!-- 溫度 降水概率 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal">
<!-- 溫度 -->
<TextView
android:id="@+id/forecastTemp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center" />
<!-- 下雨概率 -->
<TextView
android:id="@+id/forecastPop"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center" />
</LinearLayout>
<!-- 風(fēng)力 -->
<TextView
android:id="@+id/forecastWind"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center" />
</LinearLayout>
</LinearLayout>
然后新建一個(gè)WeatherAdapter繼承于ArrayAdapter< HourlyWeather>
只要重寫(xiě)getView(int position, View convertView, ViewGroup parent)方法即可
public class WeatherAdapter extends ArrayAdapter<HourlyWeather> {
private int resourceId;
private Context context;
public WeatherAdapter(Context context, int textViewResourceId, List<HourlyWeather> objects) {
super(context, textViewResourceId, objects);
this.context = context;
this.resourceId = textViewResourceId;
}
public View getView(int position, View convertView, ViewGroup parent) {
HourlyWeather weather = getItem(position);
View view = LayoutInflater.from(context).inflate(resourceId, null);
TextView forecastTime = (TextView) view.findViewById(R.id.forecastTime);
TextView forecastTemp = (TextView) view.findViewById(R.id.forecastTemp);
TextView forecastPop = (TextView) view.findViewById(R.id.forecastPop);
TextView forecastWind = (TextView) view.findViewById(R.id.forecastWind);
forecastTime.setText(weather.getTime());
forecastTemp.setText(weather.getTemp());
forecastPop.setText(weather.getPop());
forecastWind.setText(weather.getWind());
return view;
}
}
七、Activity的編寫(xiě)
首先要完成地區(qū)選擇界面ChooseAreaActivity
ChooseAreaActivity的界面僅包括一個(gè)居中的TextView和一個(gè)ListView
布局文件如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="24sp" /> </RelativeLayout> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
ChooseAreaActivity 需要完成的操作有:完成地區(qū)列表的加載、將選擇的County名傳遞給WeatherActivity
此外,當(dāng)中使用了showProgressDialog()來(lái)呈現(xiàn)一個(gè)進(jìn)度對(duì)話框,也設(shè)為無(wú)法通過(guò)返回鍵關(guān)閉,而我又沒(méi)有在弱網(wǎng)環(huán)境下調(diào)試過(guò),每次加載都是很快,也沒(méi)見(jiàn)到對(duì)話框出來(lái)過(guò),所以也不知道showProgressDialog()到底有沒(méi)有bug啥的~
public class ChooseAreaActivity extends AppCompatActivity {
// 標(biāo)記當(dāng)前列表為省份
public static final int LEVEL_PROVINCE = 0;
// 標(biāo)記當(dāng)前列表為城市
public static final int LEVEL_CITY = 1;
// 標(biāo)記當(dāng)前列表為縣
public static final int LEVEL_COUNTY = 2;
// 進(jìn)度對(duì)話框
private ProgressDialog progressDialog;
// 標(biāo)題欄
private TextView titleText;
// 數(shù)據(jù)列表
private ListView listView;
// 列表數(shù)據(jù)
private ArrayAdapter<String> adapter;
// 數(shù)據(jù)庫(kù)
private WeatherDB weatherDB;
private List<String> dataList;
private List<Province> provinceList;
private List<City> cityList;
private List<County> countyList;
//選擇的省份
private Province selectedProvince;
//選擇的城市
private City selectedCity;
//當(dāng)前選擇的列表類型
private int currentLevel;
//標(biāo)記是否從WeatherActivity跳轉(zhuǎn)而來(lái)的
private boolean isFromWeatherActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isFromWeatherActivity = getIntent().getBooleanExtra("ChooseArea", false);
SharedPreferences sharedPreferences = getSharedPreferences("Weather", Context.MODE_PRIVATE);
// 如果country已選擇且本Activity不是從天氣界面啟動(dòng)而來(lái)的,則直接跳轉(zhuǎn)到WeatherActivity
if (!TextUtils.isEmpty(sharedPreferences.getString("CountyName", "")) && !isFromWeatherActivity) {
Intent intent = new Intent(this, WeatherActivity.class);
startActivity(intent);
finish();
return;
}
setContentView(R.layout.activity_choose_area);
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
listView = (ListView) findViewById(R.id.listView);
titleText = (TextView) findViewById(R.id.title);
dataList = new ArrayList<>();
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);
listView.setAdapter(adapter);
weatherDB = WeatherDB.getInstance(this);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int index, long arg3) {
if (currentLevel == LEVEL_PROVINCE) {
selectedProvince = provinceList.get(index);
queryCities();
} else if (currentLevel == LEVEL_CITY) {
selectedCity = cityList.get(index);
queryCounties();
} else if (currentLevel == LEVEL_COUNTY) {
//當(dāng)點(diǎn)擊到縣列表時(shí),就利用Intent跳轉(zhuǎn)到天氣信息界面
String countyName = countyList.get(index).getCountyName();
Intent intent = new Intent(ChooseAreaActivity.this, WeatherActivity.class);
intent.putExtra("CountyName", countyName);
startActivity(intent);
finish();
}
}
});
queryProvinces();
}
private void queryProvinces() {
showProgressDialog();
provinceList = weatherDB.getAllProvince();
if (provinceList.size() > 0) {
dataList.clear();
for (Province province : provinceList) {
dataList.add(province.getProvinceName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText("中國(guó)");
currentLevel = LEVEL_PROVINCE;
closeProgressDialog();
} else {
queryFromServer(null, "province");
}
}
private void queryCities() {
showProgressDialog();
cityList = weatherDB.getAllCity(selectedProvince.getProvinceId());
if (cityList.size() > 0) {
dataList.clear();
for (City city : cityList) {
dataList.add(city.getCityName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText(selectedProvince.getProvinceName());
currentLevel = LEVEL_CITY;
closeProgressDialog();
} else {
queryFromServer(selectedProvince.getProvinceId(), "city");
}
}
private void queryCounties() {
showProgressDialog();
countyList = weatherDB.getAllCountry(selectedCity.getCityId());
if (countyList.size() > 0) {
dataList.clear();
for (County county : countyList) {
dataList.add(county.getCountyName());
}
adapter.notifyDataSetChanged();
listView.setSelection(0);
titleText.setText(selectedCity.getCityName());
currentLevel = LEVEL_COUNTY;
closeProgressDialog();
} else {
queryFromServer(selectedCity.getCityId(), "county");
}
}
private void queryFromServer(final String code, final String type) {
String address;
// code不為空
if (!TextUtils.isEmpty(code)) {
address = "http://www.weather.com.cn/data/list3/city" + code + ".xml";
} else {
address = "http://www.weather.com.cn/data/list3/city.xml";
}
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
boolean result = false;
if ("province".equals(type)) {
result = Utility.saveProvincesResponse(weatherDB, response);
} else if ("city".equals(type)) {
result = Utility.saveCitiesResponse(weatherDB, response, selectedProvince.getProvinceId());
} else if ("county".equals(type)) {
result = Utility.saveCountiesResponse(weatherDB, response, selectedCity.getCityId());
}
if (result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if ("province".equals(type)) {
queryProvinces();
} else if ("city".equals(type)) {
queryCities();
} else if ("county".equals(type)) {
queryCounties();
}
}
});
}
}
@Override
public void onError(Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(ChooseAreaActivity.this, "加載失敗", Toast.LENGTH_SHORT).show();
}
});
}
});
closeProgressDialog();
}
private void showProgressDialog() {
if (progressDialog == null) {
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("正在加載……");
progressDialog.setCanceledOnTouchOutside(false);
}
progressDialog.show();
}
private void closeProgressDialog() {
if (progressDialog != null) {
progressDialog.dismiss();
}
}
@Override
public void onBackPressed() {
if (currentLevel == LEVEL_COUNTY) {
queryCities();
} else if (currentLevel == LEVEL_CITY) {
queryProvinces();
} else {
if (isFromWeatherActivity) {
Intent intent = new Intent(this, WeatherActivity.class);
startActivity(intent);
}
finish();
}
}
}
WeatherActivity的布局相對(duì)復(fù)雜些,包含了許多個(gè)TextView,我也只是想著簡(jiǎn)單就好,就簡(jiǎn)單地把數(shù)據(jù)用文本呈現(xiàn)出來(lái)
// 城市切換按鈕
private Button citySwitch;
// 刷新數(shù)據(jù)按鈕
private Button weatherRefresh;
// 城市名
private TextView cityName;
// 白天夜晚天氣描敘
private TextView DayNightWeather;
// 溫度
private TextView temp;
// 日出時(shí)間
private TextView sunriseTime;
// 日落時(shí)間
private TextView sunsetTime;
// 風(fēng)力
private TextView wind;
// 降水概率
private TextView pop;
// 發(fā)布時(shí)間
private TextView updateTime;
// 今日天氣預(yù)測(cè)列表
private ListView listview;
public static List<HourlyWeather> weatherList = new ArrayList<>();
private SharedPreferences sharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.weather);
if (getSupportActionBar() != null) {
getSupportActionBar().hide();
}
init();
}
private void init() {
citySwitch = (Button) findViewById(R.id.citySwitch);
weatherRefresh = (Button) findViewById(R.id.weatherRefresh);
citySwitch.setOnClickListener(this);
weatherRefresh.setOnClickListener(this);
cityName = (TextView) findViewById(R.id.cityName);
DayNightWeather = (TextView) findViewById(R.id.DayNightWeather);
temp = (TextView) findViewById(R.id.temp);
sunriseTime = (TextView) findViewById(R.id.sunriseTime);
sunsetTime = (TextView) findViewById(R.id.sunsetTime);
wind = (TextView) findViewById(R.id.wind);
pop = (TextView) findViewById(R.id.pop);
updateTime = (TextView) findViewById(R.id.updateTime);
listview = (ListView) findViewById(R.id.hourlyForecast);
sharedPreferences = getSharedPreferences("Weather", Context.MODE_PRIVATE);
String countyName = getIntent().getStringExtra("CountyName");
// 當(dāng)countyName不為空
if (!TextUtils.isEmpty(countyName)) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("CountyName", countyName);
editor.commit();
} else {
countyName = sharedPreferences.getString("CountyName", "");
}
weatherRefresh.setText("同步中……");
queryFromServer(countyName);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.citySwitch:
Intent intent = new Intent(this, ChooseAreaActivity.class);
intent.putExtra("ChooseArea", true);
startActivity(intent);
finish();
break;
case R.id.weatherRefresh:
weatherRefresh.setText("同步中……");
String countyName = sharedPreferences.getString("CountyName", "");
if (!TextUtils.isEmpty(countyName)) {
queryFromServer(countyName);
}
break;
}
}
private void queryFromServer(final String countyName) {
try {
String url = "http://apis.baidu.com/heweather/weather/free?city=";
String name = new String(countyName.getBytes("UTF-8"), "iso-8859-1");
HttpUtil.sendHttpRequest(url + name, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
Utility.handleWeatherResponse(WeatherActivity.this, response);
runOnUiThread(new Runnable() {
@Override
public void run() {
showWeather();
}
});
}
@Override
public void onError(Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(WeatherActivity.this, "同步失敗", Toast.LENGTH_LONG).show();
weatherRefresh.setText("更新數(shù)據(jù)");
}
});
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void showWeather() {
cityName.setText(sharedPreferences.getString("cityName", "未知"));
sunriseTime.setText("日出:" + sharedPreferences.getString("sunriseTime", "未知"));
sunsetTime.setText("日落:" + sharedPreferences.getString("sunsetTime", "未知"));
DayNightWeather.setText("日:" + sharedPreferences.getString("dayWeather", "未知") + " 夜:" + sharedPreferences.getString("nightWeather", "未知"));
temp.setText("溫度:" + sharedPreferences.getString("temp", "未知"));
wind.setText("風(fēng)力:" + sharedPreferences.getString("wind", "未知"));
pop.setText("降水概率:" + sharedPreferences.getString("pop", "未知"));
updateTime.setText("發(fā)布時(shí)間:" + sharedPreferences.getString("updateTime", "未知"));
WeatherAdapter adapter = new WeatherAdapter(this, R.layout.hourly_weather, weatherList);
listview.setAdapter(adapter);
Toast.makeText(WeatherActivity.this, "已經(jīng)是最新數(shù)據(jù)了", Toast.LENGTH_SHORT).show();
weatherRefresh.setText("更新數(shù)據(jù)");
}
}
八、說(shuō)明
很奇怪的是,這個(gè)小app在我的4.4版本的小米手機(jī)上運(yùn)行無(wú)誤,可在5.1系統(tǒng)的模擬器和華為手機(jī)上卻提示無(wú)法獲取到數(shù)據(jù),返回的JSON數(shù)據(jù)提示說(shuō)城市未知,查看了很久也沒(méi)搞明白,只能作罷~~
代碼下載地址:Android簡(jiǎn)易版天氣預(yù)報(bào)app的實(shí)現(xiàn)(改進(jìn)版)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 如何通過(guò)Android Stduio來(lái)編寫(xiě)一個(gè)完整的天氣預(yù)報(bào)APP
- Android Internet應(yīng)用實(shí)現(xiàn)獲取天氣預(yù)報(bào)的示例代碼
- Android編程實(shí)現(xiàn)類似天氣預(yù)報(bào)圖文字幕垂直滾動(dòng)效果的方法
- android JSON解析數(shù)據(jù) android解析天氣預(yù)報(bào)
- Android編程實(shí)現(xiàn)獲取新浪天氣預(yù)報(bào)數(shù)據(jù)的方法
- Android天氣預(yù)報(bào)之基于HttpGet對(duì)象解析天氣數(shù)據(jù)的方法
- android調(diào)用國(guó)家氣象局天氣預(yù)報(bào)接口json數(shù)據(jù)格式解釋
- Android簡(jiǎn)單實(shí)現(xiàn)天氣預(yù)報(bào)App
相關(guān)文章
Android利用SurfaceView實(shí)現(xiàn)下雨的天氣動(dòng)畫(huà)效果
這篇文章主要介紹了Android利用SurfaceView實(shí)現(xiàn)下雨天氣效果的相關(guān)資料,文中詳細(xì)介紹 SurfaceView 和 View 的區(qū)別,以及一些需要使用到 SurfaceView 的場(chǎng)景。需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-03-03
詳解Android studio實(shí)現(xiàn)語(yǔ)音轉(zhuǎn)文字功能
這篇文章主要介紹了如何通過(guò)Android studio調(diào)用科大訊飛的語(yǔ)音轉(zhuǎn)文字功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03
在Linux下通過(guò)命令行打包Android應(yīng)用的方法
這篇文章主要介紹了在Linux下通過(guò)命令行打包Android應(yīng)用的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Android編程監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)改變的方法
這篇文章主要介紹了Android編程監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)改變的方法,基于BroadcastReceiver實(shí)現(xiàn)針對(duì)網(wǎng)絡(luò)連接狀態(tài)的監(jiān)聽(tīng)功能,需要的朋友可以參考下2017-06-06
Android Flutter實(shí)現(xiàn)圖片滑動(dòng)切換效果
Flutter 為了簡(jiǎn)化開(kāi)發(fā),提供了不少轉(zhuǎn)換動(dòng)畫(huà)組件,這類組件通常命名為 xxTransition。本篇要介紹的就是 SlideTransition,并用它實(shí)現(xiàn)圖片滑動(dòng)切換效果,感興趣的可以了解一下2022-04-04
全面解析Android的開(kāi)源圖片框架Universal-Image-Loader
這篇文章主要介紹了Android的開(kāi)源圖片框架Universal-Image-Loader,Universal-Image-Loader在GitHub上開(kāi)源,其提供的圖片加載功能令人印象相當(dāng)深刻,需要的朋友可以參考下2016-04-04
Android 打開(kāi)相冊(cè)選擇單張圖片實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 打開(kāi)相冊(cè)選擇單張圖片實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05
android跑馬燈出現(xiàn)重復(fù)跳動(dòng)以及不滾動(dòng)問(wèn)題的解決方法
這篇文章主要介紹了android跑馬燈出現(xiàn)重復(fù)跳動(dòng)以及不滾動(dòng)問(wèn)題的解決方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09

