实战开发-Ashurol天气预报APP(二)

来源:互联网 发布:不加群提取群成员软件 编辑:程序博客网 时间:2024/06/01 10:02

  做什么事都需要找到支点,想要有不断坚持的热情,就得不断更换新的支点。

 在进行第二阶段代码编写前,需要提前补充下新知识。如前面所说,这个程序是通过访问天气网的服务器来免费获取数据的,这里将提前展示下一般访问服务器返回的数据类型。

如访问 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() {@Overridepublic void run() {// TODO Auto-generated method stubHttpURLConnection 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 exceptionif(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;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.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() {@Overridepublic void onItemClick(AdapterView<?> arg0, View view, int index,long arg3) {// TODO Auto-generated method stubif(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() {@Overridepublic void onFinish(String response) {// TODO Auto-generated method stubboolean 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() {@Overridepublic void run() {// TODO Auto-generated method stubcloseProgressDialog();if("province".equals(type)){queryProvinces();}else if("city".equals(type)){queryCities();}else if("county".equals(type)){queryCounties();}}});}}@Overridepublic void onError(Exception e) {// TODO Auto-generated method stub//通过runOnUiThread()回到主线程处理逻辑runOnUiThread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stubcloseProgressDialog();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作为第一活动启动项,这里就不给代码了。

现在可以运行下程序,如果程序没错,界面应该如下图所示:


好了,第二阶段的代码也完成了,休息一下,准备第三阶段的学习!


0 0