空空天气预报
之前写了一款基于Android的空空天气预报,能够查看全国各个省、市、县的未来三天的天气预报,有穿衣指数和运动指数建议;
最近准备找工作了,现在重新回顾一下思路。
主要用到的知识有解析xml技术,解析json,mvc架构。
1、首先看下界面结果;
2、代码思路
2-1 代码架构图
2-2 设计思路
2-2-1 首先解析一个省市县xml文件,里面的代码样式如图:
<province id="01" name="北京"> <city id="0101" name="北京"> <county id="010101" name="北京" weatherCode="101010100"/> <county id="010102" name="海淀" weatherCode="101010200"/> <county id="010103" name="朝阳" weatherCode="101010300"/> <county id="010104" name="顺义" weatherCode="101010400"/> <county id="010105" name="怀柔" weatherCode="101010500"/> <county id="010106" name="通州" weatherCode="101010600"/> <county id="010107" name="昌平" weatherCode="101010700"/> <county id="010108" name="延庆" weatherCode="101010800"/> <county id="010109" name="丰台" weatherCode="101010900"/> <county id="010110" name="石景山" weatherCode="101011000"/> <county id="010111" name="大兴" weatherCode="101011100"/> <county id="010112" name="房山" weatherCode="101011200"/> <county id="010113" name="密云" weatherCode="101011300"/> <county id="010114" name="门头沟" weatherCode="101011400"/> <county id="010115" name="平谷" weatherCode="101011500"/> </city> </province>
2-2-2 模型层的建立
主要是在model层里面建立City、County、Province三个类,下面贴一部分代码:
public class County { private int id;//县城主id private String countyName;//县城名 private String countyCode;//县城代码 private int cityId;//城市id ... public class City { private int id;//城市id private String cityName;//城市名称 private String cityCode;//城市代码 private int provinceId;//省id ... public class Province { private int id;//省id private String provinceName;//省名 private String provinceCode;//省代码 public int getId() { ...
2-2-3 数据库的设计
设计两个类,一个类是CoolWeatherOpenHelper 继承自 SQLiteOpenHelper,用于数据库的创建和更新,大致代码如下:
public class CoolWeatherOpenHelper extends SQLiteOpenHelper { public CoolWeatherOpenHelper(Context context, String name,CursorFactory factory, int version) { super(context, name, factory, version); } //创建省、市、县的sql语句 public static final String CREATE_PROVINCE = "create table Province (" + "id integer primary key autoincrement," + "provinceName text," + "provinceCode text)"; public static final String CREATE_CITY = "create table City (" + "id integer primary key autoincrement," + "cityName text," + "cityCode text," + "provinceId integer)"; public static final String CREATE_COUNTY = "create table County (" + "id integer primary key autoincrement," + "countyName text," + "countyCode text," + "cityId integer)"; //创建数据库 @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_PROVINCE); //create tables db.execSQL(CREATE_CITY); db.execSQL(CREATE_COUNTY); } ...
设计第二个类CoolWeatherDB,用于插入xml文件中省市县的天气预报代码
1 public class CoolWeatherDB { 2 /** 3 * database name 数据库名 4 */ 5 public static final String DB_NAME = "cool_weather.db"; 6 /** 7 * database version ,database is used for operating the database (insert , update, delete, select) 8 */ 9 public static final int VERSION = 1; 10 private static CoolWeatherDB coolWeatherDB; 11 private SQLiteDatabase db; 12 13 /** 14 * constructor构造函数 15 */ 16 private CoolWeatherDB(Context context) { 17 CoolWeatherOpenHelper dbHelper = new CoolWeatherOpenHelper(context, DB_NAME, null, VERSION); 18 db = dbHelper.getWritableDatabase();//call onCreate method 19 } 20 /** 21 * get the instance of CoolWeatherDB, use the singleInstance modern 22 * 获得CoolWeatherDB对象,这里使用了单例模式 23 */ 24 public synchronized static CoolWeatherDB getInstance(Context context) { 25 if(coolWeatherDB == null) 26 coolWeatherDB = new CoolWeatherDB(context); 27 return coolWeatherDB; 28 } 29 /** 30 * insert province information to the database 31 * 插入省信息到数据库中 32 */ 33 public void saveProvince(Province province) { 34 if(province != null) { 35 ContentValues values = new ContentValues(); 36 values.put("provinceName", province.getProvinceName()); 37 values.put("provinceCode", province.getProvinceCode()); 38 db.insert("Province", null, values); 39 } 40 } 41 /** 42 * insert a list of provinces 43 * 插入一系列省信息到数据库中 44 * @param provinces 45 */ 46 public void saveMangProvinces(ArrayList<Province> provinces) { 47 if(provinces.size() > 0) { 48 for(Province province: provinces) { 49 ContentValues values = new ContentValues(); 50 values.put("provinceName", province.getProvinceName()); 51 values.put("provinceCode", province.getProvinceCode()); 52 db.insert("Province", null, values); 53 } 54 } 55 } 56 /** 57 * save city 58 * 插入城市信息到数据库中 59 */ 60 public void saveCity(City city) { 61 if(city != null) { 62 ContentValues values = new ContentValues(); 63 values.put("cityName", city.getCityName()); 64 values.put("cityCode", city.getCityCode()); 65 values.put("provinceId", city.getProvinceId()); 66 db.insert("City", null, values); 67 } 68 } 69 /** 70 * insert a list of cities 71 * 插入一系列的城市信息到数据库中 72 * @param cities 73 */ 74 public void saveMangCities(ArrayList<City> cities) { 75 76 if(cities.size() > 0) { 77 for(City city: cities) { 78 ContentValues values = new ContentValues(); 79 values.put("cityName", city.getCityName()); 80 values.put("cityCode", city.getCityCode()); 81 values.put("provinceId", city.getProvinceId()); 82 db.insert("City", null, values); 83 } 84 } 85 } 86 /** 87 * save county 88 * 插入县信息到数据库中 89 */ 90 public void saveCounty(County county) { 91 if(county != null) { 92 ContentValues values = new ContentValues(); 93 values.put("countyName", county.getCountyName()); 94 values.put("countyCode", county.getCountyCode()); 95 values.put("cityId", county.getCityId()); 96 db.insert("County", null, values); 97 } 98 } 99 /** 100 * 插入一系列县信息到数据库中 101 * @param counties 102 */ 103 public void saveMangCounties(ArrayList<County> counties) { 104 if(counties.size() > 0) { 105 for(County county: counties) { 106 ContentValues values = new ContentValues(); 107 values.put("countyName", county.getCountyName()); 108 values.put("countyCode", county.getCountyCode()); 109 values.put("cityId", county.getCityId()); 110 db.insert("County", null, values); 111 } 112 } 113 } 114 /** 115 * get province infor from the db 116 * 从数据库中获取省信息 117 */ 118 public List<Province> loadProvinces() { 119 List<Province> list = new ArrayList<Province>(); 120 Cursor cursor = db.query("Province", null, null, null, null, null, null); 121 if(cursor.moveToFirst()) { 122 do{ 123 Province province = new Province(); 124 province.setId(cursor.getInt(cursor.getColumnIndex("id"))); 125 province.setProvinceName(cursor.getString(cursor.getColumnIndex("provinceName"))); 126 province.setProvinceCode(cursor.getString(cursor.getColumnIndex("provinceCode"))); 127 list.add(province); 128 }while(cursor.moveToNext()); 129 } 130 return list; 131 } 132 /** 133 * get city infor from the db by provinceId 134 * 根据省id从数据库中获取城市信息 135 */ 136 public List<City> loadCities(int provinceId) { 137 List<City> list = new ArrayList<City>(); 138 Cursor cursor = db.query("City", null, "provinceId=?", new String[] {String.valueOf(provinceId)}, null, null, null); 139 if(cursor.moveToFirst()) { 140 do{ 141 City city= new City(); 142 city.setId(cursor.getInt(cursor.getColumnIndex("id"))); 143 city.setCityName(cursor.getString(cursor.getColumnIndex("cityName"))); 144 city.setCityCode(cursor.getString(cursor.getColumnIndex("cityCode"))); 145 list.add(city); 146 }while(cursor.moveToNext()); 147 } 148 return list; 149 } 150 /** 151 * get county information from the database by cityId 152 * 根据城市id从数据库中获取县城信息 153 */ 154 public List<County> loadCounties(String cityCode) { 155 List<County> list = new ArrayList<County>(); 156 Cursor cursor = db.query("County", null, "cityId=?", new String[]{String.valueOf(cityCode)}, null, null, null); 157 if(cursor.moveToFirst()) { 158 do{ 159 County county = new County(); 160 county.setId(cursor.getInt(cursor.getColumnIndex("id"))); 161 county.setCountyName(cursor.getString(cursor.getColumnIndex("countyName"))); 162 county.setCountyCode(cursor.getString(cursor.getColumnIndex("countyCode"))); 163 county.setCityId(Integer.parseInt(cityCode)); 164 list.add(county); 165 }while(cursor.moveToNext()); 166 } 167 return list; 168 } 169 }
2-2-4 设计解析xml工具类ParseCity
1 //用于解析xml中的省市县及对应的代码 2 public class ParseCity extends Thread { 3 public static void parse(final InputStream in,final CoolWeatherDB coolWeatherDB) { 4 new Thread(new Runnable() { 5 ArrayList<Province> provinces = new ArrayList<Province>();// ArrayList存储多个省 6 ArrayList<City> cities = new ArrayList<City>();// ArrayList存储多个市 7 ArrayList<County> counties = new ArrayList<County>();// ArrayList存储多个县 8 @Override 9 public void run() { 10 try { 11 // 用XmlPullParserFactory去解析xml文件,获取实例 12 XmlPullParserFactory factory = XmlPullParserFactory 13 .newInstance(); 14 // 获取XmlPullParser类型的实例 15 XmlPullParser xmlPullParser = factory.newPullParser(); 16 // 设置输入流和编码类型 17 xmlPullParser.setInput(in, "utf-8");// set stream 18 // 获取事件的类型 19 int eventType = xmlPullParser.getEventType(); 20 // 获得的是事件类型,常用的有:XmlPullParser.END_DOCUMENT(xml文档结束) 21 // XmlPullParser.START_DOCUMENT(文档开始),XmlPullParser.START_TAG(开始标签) 22 // XmlPullParser.END_TAG(结束标签).XmlPullParser.TEXT(内容) 23 String cityid = "";// 城市的id 24 String fid = "";//设置外键的值 25 // 使用while循环,解析完整个xml文件 26 while (eventType != XmlPullParser.END_DOCUMENT) { 27 String nodeName = xmlPullParser.getName(); 28 switch (eventType) { 29 // begin to parse somenode 开始解析某个结点 30 case XmlPullParser.START_DOCUMENT: 31 break; 32 case XmlPullParser.START_TAG: { 33 //获取xml中的标记名称,有Province、City、County这三种 34 String tagName = xmlPullParser.getName(); 35 //如果是省的话 36 if ("province".equals(tagName)) { 37 // get the count of province 38 int count = xmlPullParser.getAttributeCount(); 39 String provinceId = null;//省id 40 String provinceName = null;//省名称 41 for (int i = 0; i < count; i++) { 42 // 获取id 43 String attrName = xmlPullParser.getAttributeName(i); 44 // 获取省名称 北京 45 String attrValue = xmlPullParser.getAttributeValue(i); 46 if ("id".equals(attrName)) { 47 // 用于设置在城市中的外键 48 fid = attrValue; 49 provinceId = attrValue; 50 } else if ("name".equals(attrName)) { 51 provinceName = attrValue; 52 } 53 } 54 Province province = new Province(); 55 province.setProvinceCode(provinceId); 56 province.setProvinceName(provinceName); 57 // put the parsed data into the list provinces 58 //保存省信息到数据库中 59 coolWeatherDB.saveProvince(province); 60 } 61 //如果是城市 62 if ("city".equals(tagName)) { 63 //获取城市的数量 64 int count = xmlPullParser.getAttributeCount(); 65 //城市名称 66 String cityName = null; 67 //城市代码 68 String cityCode = null; 69 //城市的外键,省id 70 int provinceId = Integer.parseInt(fid); 71 for (int i = 0; i < count; i++) { 72 String attrName = xmlPullParser.getAttributeName(i); 73 String attrValue = xmlPullParser 74 .getAttributeValue(i); 75 if ("id".equals(attrName)) { 76 cityid = attrValue;// set foreign id in 77 cityCode = attrValue;//城市代码 78 } else if ("name".equals(attrName)) { 79 cityName = attrValue;//城市名称 80 } 81 } 82 City city = new City(); 83 city.setCityCode(cityCode); 84 city.setCityName(cityName); 85 city.setProvinceId(provinceId); 86 //在数据库中保存城市 87 coolWeatherDB.saveCity(city); 88 // cities.add(city); 89 } 90 if ("county".equals(nodeName)) { 91 int count = xmlPullParser.getAttributeCount(); 92 String countyName = null;//县名称 93 String countyCode = null;//县代号 94 int cityId = Integer.parseInt(cityid);//获得城市id 95 for (int i = 0; i < count; i++) { 96 String attrName = xmlPullParser.getAttributeName(i); 97 String attrValue = xmlPullParser.getAttributeValue(i); 98 if ("id".equals(attrName)) { 99 } else if ("name".equals(attrName)) { 100 countyName = attrValue; 101 } else if ("weatherCode".equals(attrName)) { 102 countyCode = attrValue; 103 } 104 } 105 County county = new County(); 106 county.setCountyCode(countyCode); 107 county.setCountyName(countyName); 108 county.setCityId(cityId); 109 //保存县值 110 coolWeatherDB.saveCounty(county); 111 // counties.add(county); 112 } 113 break; 114 } 115 case XmlPullParser.END_TAG: 116 break; 117 case XmlPullParser.END_DOCUMENT: 118 /* 119 * coolWeatherDB.saveMangProvinces(provinces); 120 * coolWeatherDB.saveMangCities(cities); 121 * coolWeatherDB.saveMangCounties(counties); 122 */ 123 break; 124 default: 125 break; 126 } 127 eventType = xmlPullParser.next(); 128 } 129 } catch (Exception e) { 130 e.printStackTrace(); 131 } 132 } 133 }).start(); 134 135 } 136 }
2-2-5 选择城市界面
2-2-5-1 首先判断数据库中的内容是否为空,如果为空,那么就去调用ParseCity.parse(open,coolWeatherDB)的方法,如下
try { //如果数据库为空,那么从xml文件中获得城市代号数据 if(coolWeatherDB.loadProvinces().isEmpty()) {//get infor from xml showProgressDialog();//显示正在加载 InputStream open = this.getResources().getAssets().open("city.xml"); //调用parseCity ParseCity.parse(open,coolWeatherDB); } } catch (IOException e) { e.printStackTrace(); }
2-2-5-2 获取数据后,默认显示省级别的城市,当点击其中一个城市后,在改ListView里面的点击事件去处理对应的逻辑,比如点击四川省,就去查询四川省下的城市,点击成都,就去查询成都下面的城市。
listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int index,long arg3) { if(currentLevel == LEVEL_RPOVINCE) { selectedProvince = provinceList.get(index); queryCities(); }else if(currentLevel == LEVEL_CITY) { selectedCity = cityList.get(index); queryCounties(); }else if(currentLevel == LEVEL_COUNTY) { String countyCode = countyList.get(index).getCountyCode();
//跳转到天气查询界面 Intent intent = new Intent(ChooseAreaActivity.this,WeatherActivity.class); intent.putExtra("county_code", countyCode); startActivity(intent); finish(); } } });
下面举一个查询的例子,比如查询省下面的城市,调用queryCities()方法:
private void queryCities() { cityList = coolWeatherDB.loadCities(selectedProvince.getId());//根据省id查询城市列表 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; } }
2-2-5-3 如果查询到了最后一个级别,那么跳转到WeatherActivity界面,传递城市代号过去。
String countyCode = countyList.get(index).getCountyCode(); //跳转到天气查询界面 Intent intent = new Intent(ChooseAreaActivity.this,WeatherActivity.class); intent.putExtra("county_code", countyCode); startActivity(intent); finish();
2-2-5-4 WeatherActivity界面代码解析
//获取从ChooseAreaActitivty界面传过来的参数值 String countyCode = getIntent().getStringExtra("county_code");
if(!TextUtils.isEmpty(countyCode)) { data_y.setText("同步中..."); weatherInfoLayout.setVisibility(View.INVISIBLE); cityNameText.setVisibility(View.INVISIBLE); //查询天气信息 queryWeatherCode(countyCode); //queryWeatherInfor(countyCode); //new ConnectNetThread(WeatherActivity.this, countyCode).start(); }
private void queryWeatherCode(String countyCode) { //查询天气预报网址 String address = "http://weatherapi.market.xiaomi.com/wtr-v2/weather?cityId=" + countyCode; //查询 queryFromServer(address,"weatherCode"); } private void queryFromServer(final String address,final String type) { HttpUtil.sendHttpRequest(address, new HttpCallbackListener() { @Override public void onFinish(String response) { if("weatherCode".equals(type)) { //回调处理response字符串,即处理天气预报返回的结果 Utility.hadnleWeatherResponse(WeatherActivity.this, response); runOnUiThread(new Runnable() { @Override public void run() { //显示天气预报信息 showWeather(); } }); } } @Override public void onError(Exception e) { // TODO Auto-generated method stub runOnUiThread(new Runnable() { @Override public void run() { data_y.setText("同步失败"); } }); } }); }
2-2-5-5 然后去HttpUtil.sendHttpRequest()方法中通过连接网络从服务器端获取json数据,回调HttpCallbackListener接口
其中HttpCallbackListener是一个接口
public interface HttpCallbackListener { void onFinish(String response);//调用完成时的方法 void onError(Exception e);//出现异常时调用的 }
HttpUtil.sendHttpRequest()的具体实现为:
public static void sendHttpRequest(final String address,final HttpCallbackListener listener) { //create a new thread to execute the method new Thread(new Runnable() { @Override public void run() { HttpURLConnection connection = null; try{ URL url = new URL(address);//初始化url对象 //通过url得到HttpURLConnection对象 connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); //获取输入字节流 InputStream in = connection.getInputStream();//get inputStream //将输入字节流转换为带缓冲的输入字符流 BufferedReader reader = new BufferedReader(new InputStreamReader(in,"UTF-8")); StringBuilder response = new StringBuilder(); String line; while((line=reader.readLine()) != null) { response.append(line); }//return response in.close(); if(listener != null) { // call back HttpCallbackListener interface: onFinish() method listener.onFinish(response.toString()); } }catch(Exception e) { if(listener != null) { //call back HttpCallbackListener interface: onError() method listener.onError(e); } }finally{ if(connection != null) { connection.disconnect(); } } } }).start(); }
HttpUtil.sendHttpRequest()方法执行完后,调用
listener.onFinish(response.toString());
然后返回到WeatherActivity中的queryFromServer方法中
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() { @Override public void onFinish(String response) { if("weatherCode".equals(type)) { //回调处理response字符串,即处理天气预报返回的结果 Utility.hadnleWeatherResponse(WeatherActivity.this, response); runOnUiThread(new Runnable() { @Override public void run() { //显示天气预报信息 showWeather(); } }); } }
接着执行Utility.hadnleWeatherResponse()的方法
public static void hadnleWeatherResponse(Context context,String response) { try { //新建JSONObject对象 JSONObject jsonObject = new JSONObject(response); //获取天气预报json对象 JSONObject weatherInfor = jsonObject.getJSONObject("forecast"); //获取json数组 JSONArray jsonArr = jsonObject.getJSONArray("index"); //int iSize = jsonArr.length(); //获取运动指数 JSONObject jsonSports = jsonArr.getJSONObject(1); //获取穿衣指数 JSONObject jsonWear = jsonArr.getJSONObject(2); String cityName = weatherInfor.getString("city"); String cityid = weatherInfor.getString("cityid");//not show String date_y = weatherInfor.getString("date_y"); String wind1 = weatherInfor.getString("wind1"); String weather_desp1 = "今日 " + weatherInfor.getString("temp1") + " " + weatherInfor.getString("weather1"); String weather_desp2 = "明日 " + weatherInfor.getString("temp2") + " " + weatherInfor.getString("weather2"); String weather_desp3 = "后日 " + weatherInfor.getString("temp3") + " " + weatherInfor.getString("weather3"); String sports = jsonSports.getString("name") + "(" + jsonSports.getString("index") + ")\n" + jsonSports.getString("details"); String wear = jsonWear.getString("name") + "(" + jsonWear.getString("index") + ")\n" + jsonWear.getString("details"); //保存天气信息到SharedPreferences的文件中 saveWeatherInfor(context, cityName, cityid, date_y, wind1, weather_desp1, weather_desp2, weather_desp3,sports,wear); } catch (JSONException e) { e.printStackTrace(); } } /** * save data infor into SharedPreferences file * 保存数据信息到SharedPreferences里面,在WeatherActivity的showWeather()方法中好调用 */ public static void saveWeatherInfor(Context context,String cityName,String cityid,String date_y,String wind1,String weather_desp1,String weather_desp2,String weather_desp3,String sports,String wear){ SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日",Locale.CHINA); editor.putBoolean("city_selected", true); editor.putString("city_name", cityName); editor.putString("cityid", cityid); editor.putString("date_y", sdf.format(new Date())); //String time = sdf.format(new Date()); editor.putString("wind1", wind1); editor.putString("weather_desp1", weather_desp1); editor.putString("weather_desp2", weather_desp2); editor.putString("weather_desp3", weather_desp3); editor.putString("sports", sports); editor.putString("wear", wear); editor.commit(); }
执行完后,再返回到queryFromServer方法中run()的showWeather()方法,显示天气信息
private void showWeather() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); cityNameText.setText(prefs.getString("city_name", "")); data_y.setText(prefs.getString("date_y", "")); wind1.setText(prefs.getString("wind1", "")); weather_desp1.setText(prefs.getString("weather_desp1", "")); weather_desp2.setText(prefs.getString("weather_desp2", "")); weather_desp3.setText(prefs.getString("weather_desp3", "")); sports.setText(prefs.getString("sports", "")); wear.setText(prefs.getString("wear", "")); weatherInfoLayout.setVisibility(View.VISIBLE); cityNameText.setVisibility(View.VISIBLE); }
这样就成功的查询到了一个城市的天气信息了。
3 整体调用关系图
最后附上代码下载地址
https://files.cnblogs.com/files/fankongkong/CoolWeather.rar