做什么事都需要找到支点,想要有不断坚持的热情,就得不断更换新的支点。
在进行第二阶段代码编写前,需要提前补充下新知识。如前面所说,这个程序是通过访问天气网的服务器来免费获取数据的,这里将提前展示下一般访问服务器返回的数据类型。
如访问 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作为第一活动启动项,这里就不给代码了。
现在可以运行下程序,如果程序没错,界面应该如下图所示:
好了,第二阶段的代码也完成了,休息一下,准备第三阶段的学习!