实战开发-Ashurol天气预报APP(二)
2016-01-02 20:14
337 查看
做什么事都需要找到支点,想要有不断坚持的热情,就得不断更换新的支点。
在进行第二阶段代码编写前,需要提前补充下新知识。如前面所说,这个程序是通过访问天气网的服务器来免费获取数据的,这里将提前展示下一般访问服务器返回的数据类型。
如访问 http://www.weather.com.cn/data/list3/city.xml,服务器将返回一段文本信息,
01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西。。。。。。。。。。。。
可以看到不同省份之间用逗号隔开,省份名称和省级代号则由竖线相隔。
那么如何知道某个省份下有哪些城市,也很简单,比如江苏的省级代号为19,则需要访问如下地址即可获得江苏省所有城市名。
http://www.weather.com.cn/data/list3/city19.xml
获得的数据格式和省份信息格式一样,举一反三,在city后添加市级代号,便能获得该市里所有县级名称及代号。有了所有县级代号,要怎么查看天气信息呢?
问题也很简单,譬如昆山县级代号为190404,那么访问http://www.weather.com.cn/data/list3/city190404.xml,这时服务器将返回
190404|101190404,这里的190404还是县级代号,101190404为昆山的天气代号。这时候再去访问天气接口:http://www.weather.com.cn/data/cityinfo/101190404.html,
服务器便会把昆山的天气信息以JSON格式返回给我们,如下所示:
{"weatherinfo":{"city":"昆山","cityid":"101190404","temp1":"15℃","temp2":"5℃","weather":"多云","img1":"d1.gif","img2":"n1.gif","ptime":"08:00"}}之后根据需要进行解析。
好了,拓展就到这里,接着进入主题,进入第二阶段的代码编写,因为多数数据是从服务器获取的,因此这里和服务器的交互必不可少,所有我们可以在util包中增加一个HttpUtil类,代码如下:
HttpUtil类中使用到HttpCallbackListener接口来回调服务器返回的结果,因此我们还需要在util包下中添加这个接口,如下所示
首先每个办法都是将对应的数据解析,接着把解析出来的数据设置到对应的实体类中,最后调用CoolWeatherDB中的三个save()方法将数据储存到表中。
接下来我们可以开始编写界面了,我们的界面相对比较简单,这里就不给解释了,代码如下。
再接下来便是关键的一步了,我们需要编写遍历省,市,县数据的活动了,在activity包里新建ChooseAreaActivity继承Activity,代码如下:
最后别忘了配置AndroidManifest.xml文件及添加网络访问权限,将ChooseAreaActivity作为第一活动启动项,这里就不给代码了。
现在可以运行下程序,如果程序没错,界面应该如下图所示:
好了,第二阶段的代码也完成了,休息一下,准备第三阶段的学习!
在进行第二阶段代码编写前,需要提前补充下新知识。如前面所说,这个程序是通过访问天气网的服务器来免费获取数据的,这里将提前展示下一般访问服务器返回的数据类型。
如访问 http://www.weather.com.cn/data/list3/city.xml,服务器将返回一段文本信息,
01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西。。。。。。。。。。。。
可以看到不同省份之间用逗号隔开,省份名称和省级代号则由竖线相隔。
那么如何知道某个省份下有哪些城市,也很简单,比如江苏的省级代号为19,则需要访问如下地址即可获得江苏省所有城市名。
http://www.weather.com.cn/data/list3/city19.xml
获得的数据格式和省份信息格式一样,举一反三,在city后添加市级代号,便能获得该市里所有县级名称及代号。有了所有县级代号,要怎么查看天气信息呢?
问题也很简单,譬如昆山县级代号为190404,那么访问http://www.weather.com.cn/data/list3/city190404.xml,这时服务器将返回
190404|101190404,这里的190404还是县级代号,101190404为昆山的天气代号。这时候再去访问天气接口:http://www.weather.com.cn/data/cityinfo/101190404.html,
服务器便会把昆山的天气信息以JSON格式返回给我们,如下所示:
{"weatherinfo":{"city":"昆山","cityid":"101190404","temp1":"15℃","temp2":"5℃","weather":"多云","img1":"d1.gif","img2":"n1.gif","ptime":"08:00"}}之后根据需要进行解析。
好了,拓展就到这里,接着进入主题,进入第二阶段的代码编写,因为多数数据是从服务器获取的,因此这里和服务器的交互必不可少,所有我们可以在util包中增加一个HttpUtil类,代码如下:
public class HttpUtil { public static void sendHttpRequest(final String address,final HttpCallbackListener listener) { new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub HttpURLConnection connection=null; try { URL url=new URL(address); connection=(HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(8000); connection.setReadTimeout(8000); InputStream in=connection.getInputStream(); BufferedReader reader=new BufferedReader(new InputStreamReader(in)); StringBuilder response=new StringBuilder(); String line; while((line=reader.readLine())!=null) { response.append(line); } if(listener!=null) { //回调onFinish()方法 listener.onFinish(response.toString()); } } catch (Exception e) { // TODO: handle exception if(listener!=null) { listener.onError(e); } }finally{ if(connection!=null) { connection.disconnect(); } } } }).start(); } }
HttpUtil类中使用到HttpCallbackListener接口来回调服务器返回的结果,因此我们还需要在util包下中添加这个接口,如下所示
public interface HttpCallbackListener { void onFinish(String response); void onError(Exception e); }另外服务器返回的省市数据都是“代号|城市,代号|城市”这种格式,所以我们最好再提供一个工具类来解析处理这些数据。在util包下新建一个Utility类,代码如下所示:
public class Utility { /* * 解析和处理服务器返回的省级数据 */ public synchronized static boolean handleProvincesResponse(CoolWeatherDB coolWeatherDB,String response) { if(!TextUtils.isEmpty(response)) { String[] allProvinces=response.split(","); if(allProvinces!=null&&allProvinces.length>0) { for(String p:allProvinces) { String[] array=p.split("\\|"); Province province=new Province(); province.setProvinceCode(array[0]); province.setProvinceName(array[1]); //将解析出来的数据储存到Province表中 coolWeatherDB.saveProvince(province); } return true; } } return false; } /* * 解析和处理服务器返回的市级数据 */ public static boolean handleCitiesResponse(CoolWeatherDB coolWeatherDB,String response,int provinceId) { if(!TextUtils.isEmpty(response)) { String[] allCities=response.split(","); if(allCities!=null&&allCities.length>0) { for(String c:allCities) { String[] array=c.split("\\|"); City city=new City(); city.setCityCode(array[0]); city.setCityName(array[1]); city.setProvinceId(provinceId); //将解析出来的数据储存到City表中 coolWeatherDB.saveCity(city); } return true; } } return false; } /* * 解析和处理返回的县级数据 */ public static boolean handleCountiesResponse(CoolWeatherDB coolWeatherDB, String response,int cityId) { if(!TextUtils.isEmpty(response)) { String[] allCounties=response.split(","); if(allCounties!=null&&allCounties.length>0) { for(String c:allCounties) { String[] array=c.split("\\|"); County county=new County(); county.setCountyCode(array[0]); county.setCountyName(array[1]); county.setCityId(cityId); //将解析出来的数据储存到County表 coolWeatherDB.saveCounty(county); } return true; } } return false; } }
首先每个办法都是将对应的数据解析,接着把解析出来的数据设置到对应的实体类中,最后调用CoolWeatherDB中的三个save()方法将数据储存到表中。
接下来我们可以开始编写界面了,我们的界面相对比较简单,这里就不给解释了,代码如下。
<?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" android:background="#484E61" > <TextView android:id="@+id/titile_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="#fff" android:textSize="24sp" /> </RelativeLayout> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </LinearLayout>
再接下来便是关键的一步了,我们需要编写遍历省,市,县数据的活动了,在activity包里新建ChooseAreaActivity继承Activity,代码如下:
public class ChooseAreaActivity extends Activity{ public static final int LEVEL_PROVINCE=0; public static final int LEVEL_CITY=1; public static final int LEVEL_COUNTY=2; private ProgressDialog progressDialog; private TextView titleText; private ListView listView; private ArrayAdapter<String> adapter; private CoolWeatherDB coolWeatherDB; /* * 是否从WeatherActivity中跳转过来 */ private boolean isFromWeatherActivity; private List<String> datalist=new ArrayList<String>(); /* * 省列表 */ private List<Province> provinceList; /* * 市列表 */ private List<City> cityList; /* * 县列表 */ private List<County> countyList; /* * 选中的省份 */ private Province selectedProvince; /* * 选中的城市 */ private City selectedCity; /* * 当前选中的级别 */ private int currentLevel; @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.choose_area); listView=(ListView) findViewById(R.id.list_view); titleText=(TextView) findViewById(R.id.titile_text); adapter=new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,datalist); listView.setAdapter(adapter); coolWeatherDB=CoolWeatherDB.getInstance(this); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View view, int index, long arg3) { // TODO Auto-generated method stub if(currentLevel==LEVEL_PROVINCE) { selectedProvince=provinceList.get(index); queryCities(); }else if(currentLevel==LEVEL_CITY) { selectedCity=cityList.get(index); queryCounties(); } } }); queryProvinces();//加载省级数据 } /* * 查询全国所有的省,优先从数据库查询,如果没有查询到再去服务器上查询 */ private void queryProvinces() { provinceList=coolWeatherDB.loadProvinces(); if(provinceList.size()>0) { datalist.clear(); for(Province province:provinceList) { datalist.add(province.getProvinceName()); } adapter.notifyDataSetChanged(); listView.setSelection(0); titleText.setText("中国"); currentLevel=LEVEL_PROVINCE; }else{ queryFromServer(null,"province"); } } /* * 查询选中生内所有的市,优先从数据库查询,如果没有查询到再到服务器查询。 */ private void queryCities() { cityList=coolWeatherDB.loadCities(selectedProvince.getId()); 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; }else{ queryFromServer(selectedProvince.getProvinceCode(),"city"); } } /* * 查询选中市内所有的县,优先从数据库查询,如果没有查询到再去服务器上查询 */ private void queryCounties() { countyList=coolWeatherDB.loadCounties(selectedCity.getId()); 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; }else{ queryFromServer(selectedCity.getCityCode(),"county"); } } /* * 根据传入的代号和类型从服务器上查询市县数据 */ private void queryFromServer(final String code,final String type) { String address; 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"; } showProgressDialog(); HttpUtil.sendHttpRequest(address, new HttpCallbackListener() { @Override public void onFinish(String response) { // TODO Auto-generated method stub boolean result=false; if("province".equals(type)) { result=Utility.handleProvincesResponse(coolWeatherDB, response); }else if("city".equals(type)) { result=Utility.handleCitiesResponse(coolWeatherDB, response, selectedProvince.getId()); }else if("county".equals(type)) { result=Utility.handleCountiesResponse(coolWeatherDB, response, selectedCity.getId()); } if(result) { //通过runOnUiThread方法回到主线程处理逻辑 runOnUiThread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub closeProgressDialog(); if("province".equals(type)) { queryProvinces(); }else if("city".equals(type)) { queryCities(); }else if("county".equals(type)) { queryCounties(); } } }); } } @Override public void onError(Exception e) { // TODO Auto-generated method stub //通过runOnUiThread()回到主线程处理逻辑 runOnUiThread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub closeProgressDialog(); Toast.makeText(ChooseAreaActivity.this, "加载失败!", Toast.LENGTH_SHORT).show(); } }); } }); } /* * 显示进度对话框 */ private void showProgressDialog() { if(progressDialog==null) { progressDialog=new ProgressDialog(this); progressDialog.setMessage("正在加载。。。"); progressDialog.setCanceledOnTouchOutside(false); //用ProgressDialog的地方,最好加下这个属性,防止4.0系统出问题。mProgressDialog.setCanceledOnTouchOutside(false); //就是在loading的时候,如果你触摸屏幕其它区域,就会让这个progressDialog消失,然后可能出现崩溃问题 } progressDialog.show(); } /* * 关闭进度对话框 */ private void closeProgressDialog() { if(progressDialog!=null) { progressDialog.dismiss(); } } /* * 捕获Back按键,根据当前级别来判断,此时应该返回市列表,省列表,还是直接退出。 */ public void onBackPressed() { if(currentLevel==LEVEL_COUNTY) { queryCities(); }else if(currentLevel==LEVEL_CITY) { queryProvinces(); }else{ finish(); } } }
最后别忘了配置AndroidManifest.xml文件及添加网络访问权限,将ChooseAreaActivity作为第一活动启动项,这里就不给代码了。
现在可以运行下程序,如果程序没错,界面应该如下图所示:
好了,第二阶段的代码也完成了,休息一下,准备第三阶段的学习!
相关文章推荐
- iOS完全自学手册——[二]Hello World工程
- android studio更换默认的ic_launcher图标问题
- IOS:被误解的MVC和被神化的MVVM
- Web app浅谈
- 联想因为Lenovo Service Engine BIOS再一次陷入麻烦
- 安卓开发黄金搭档:android-studio+Genymotion模拟器
- 隐藏android系统标题栏和状态栏
- android数据存储之SharedPreferences基础
- 实战开发-Ashurol天气预报APP(一)
- android 文字Flash特效
- android 文件存储基础
- Android -- 启动Service并传递数据
- swift-switch使用方法
- APP(iOS)测试流程
- 【Gapps】安装GooglePlay引发一系列问题
- Objective-C学习日志3
- APP开发流程
- IOS中的深拷贝和浅拷贝
- Android Studio项目中的MainActivity.xml与ContentActivity.xml
- Objective-C中的单例模式(工具类)