14、"@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击查询天气" />
在WelcomeActivity里,关联布局文件和实例化button.然后添加onClick,构造出一个intent,传入WelcomeActivity上下文,传入ChooseAreaActivity.class为目标活动。即在WelcomeActivity这个活动的基础上打开ChooseAreaActivity这个
15、活动。然后通过startActivity()方法来执行这个intent。
setContentView(R.layout.welcome_layout);
Button button1=(Button)findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener(){
public void onClick(View v){
Intent intent=new Intent(WelcomeActivity.this,ChooseAreaActivity
16、class);
startActivity(intent);
3.2.2 主工程设计
3.2.2.1 第一阶段
1.新建工程命名为coolweather。为了让项目有更好的结构。需要在com.coolweather.app包下再新建一个包,如图3.3所示。
图3.3 新建包构架
其中activity包用于存放所有活动代码,db包用于存放所有数据库相关的代码,model包用于存放所有模型相关的代码,receiver包用于存放所有广播相关的代码,service包用于存放所有服务相关的代码,util包用于存放所有工具相关的代码。
2.创建数据库和表,这样从服务器
17、获取的数据才能够存储到本地。表的设计这里建立三张表,Province,City,County,分别用于存放省,市,县的各种数据信息,三张表的建表语句如下(以county表为例)
County:
create table County(
id integer primary key autoincrement,
county_name text,
county_code text,
city_id integer)
其中id是自增长主键,county_name表示县名,county_code表示县级代号,city_id是county表关联city表的外键。
接下来将建表语句写入到
18、代码中。在db包下新建CoolWeatherOpenHelper类,用于province,city,county的建表。核心代码如下所示。
public void onCreate(SQLiteDatabase db) {
//创建表
db.execSQL(CREATE_PROVINCE);
db.execSQL(CREATE_CITY);
db.execSQL(CREATE_COUNTY);
}
3.为每张表创建一个对应的实体类。在model下分别新建Province类,city类,county类。实体类非常简单,基本就是对应字段的g
19、et和set方法。类的创建如图3.4所示。
图3.4 实体类
4.创建一个CoolWeatherDB类,这个类将会把一些常用的数据库操作封装起来,以方便后面使用。代码如下所示。定义数据库名,版本。将构造方法私有化,实例化数据库。保存加载省级、市级、县级数据信息
从提交的程序中可以看到CoolWeatherDB是一个单例类,这里将它的构造方法私有化,并提供了一个getInstance()方法来获取CoolWeatherDB的实例,这样就可以保证全局范围内只会有一个CoolWeatherDB的实例。在CoolWeatherDB中,提供了六组方法,saveProvince()、loadPr
20、ovince()、saveCity()、loadCities()、saveCounty()、loadCounties(),分别用于存储省份数据、读取所有省份数据,存储城市数据、读取某省内所有城市数据,存储县数据、读取某市内所有县的数据。
3.2.2.2 第二阶段
1.全国所有的省市县的数据都是从服务器端获取到的,所以为了使程序具有遍历全国省市县的功能,这里和服务器的交互式必不可少的。所以先在util包下增加一个HttpUtil类。因为在HttpUtil中使用到了HttpCallbackListener接口来回调服务返回的结果,因此还需要在util包下添加这个接口。另外由于服务器返回的省
21、市县数据都是“代号|城市,代号|城市,……”这种格式的,所以要再提供一个工具类来解析和处理这种数据,。在util包下再新建一个Utility类。在该类中,提供了handlepRrovince()、handleCitiesResponse()、handleCountiesResponse()这三个方法,分别用于解析和处理服务器返回的省级、市级、县级数据。解析的规则就是先按逗号分隔,再按单竖线分隔,接着将解析出来的数据设置到实体类中,最后调用CoolWeatherDB中的三个save()方法将数据存储到相应的表中。两个类和一个接口的创建如图3.5所示。
图3.5 解析数据工具类
2.工具类
22、准备好,开始布局,在res/layout目录下新建choose_area.xml布局。布局文件中的内容比较简单,先是定义一个50dp高的头布局,并在里面放置了一个TextView用于显示标题内容。然后在头布局的下面定义了一个ListView,用于显示省市县的数据。
3.编写用于遍历省市县的活动,在activity包下新建ChooseAreaActivity继承自Activity。
该类中的代码非常多,这里只把逻辑理理。在onCreate()方法中先是获取到了一些控件的实例,然后去初始化了ArrayAdapter,将它设置为ListView的适配器。之后又去获取到了CoolWeatherDB
23、的实例,并给ListView设置了点击事件,到这,初始化工作完成。
在onCreate()方法的最后调用了queryProvince()方法,也就是从这里开始加载省级数据的。queryProvinces()方法的内部会首先调用CoolWeatherDB和loadProvinces()方法来从数据库读取省级数据。如果读取到了就直接将数据显示到界面上,如果没有读取到就调用queryFromServer()方法从服务器上查询数据。
queryFromServer()方法会先根据传入的参数来拼装查询地址,现在网上有不少免费的天气预报接口可以实现上述功能,例如新浪天气,百度天气的,这里我准备使用中国
24、天气网提供的API,接口来实现上述功能。
比如要想,努力出中国所有的省份,只需访问如下地址,这里需要注意,如果用浏览器直接访问的话,可能会得到一个错误提示。这是因为浏览器认为服务器应该返回一个xml格式的数据。但实际上服务器返回的数据,并不是xml格式所导致的,右键查看网页源码代码就可以看到服务器返回的真是数据了:
服务器会返回一段文本信息,其中包含了中国所有的省份名称以及省级代号,如下
01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西,12|山东,13|新疆,14|西藏,15|青海,16|甘肃,1
25、7|宁夏,18|河南,19|江苏,20|湖北,21|浙江,22|安徽,23|福建,24|江西,25|湖南,26|贵州,27|四川,28|广东,29|云南,30|广西,31|海南,32|香港,33|澳门,34|台湾
可以看到北京的代号是01,上海的代号是02,不同省份之间以逗号隔开,省份名称和省级代号之间用单竖线分隔。要想知道某个省内有哪些城市,比如江苏的代号是19,那么只要访问如下地址:
这次服务器返回的数据如下:
190401|苏州,190402|常熟,190403|张家港,190404|昆山,190405|吴县东山,190406|吴县,190407|吴江,190408|太仓
通
26、过这种方式,就能把全国所有的省,市,县,都罗列出来了。那么解决了全国省市县数据的获取,然后就是解决查询天气信息的问题。这里原理同上,比如,昆山的县级代号是190404,那么访问如下地址:
这时服务器返回的数据非常简短:
190404|101190404
其中后半部分的101190404就是昆山所对应的天气代号了。这个时候再去访问查询天气接口,将相应的天气代号填入即可,接口地址如下:
这样服务器就会把昆山当前的天气信息以JSON格式返回给我们了,如下所示:
{“weatherinfo”:
“city”:昆山,”cityid”:101190404,”temp1”:”21℃”,”
27、temp2”:”9℃”,”weather”:”多云转小雨”,”img1”:”d1.gif”,”img2”:”n7.gif”,”ptime”:”11:00”}
其中city表示城市名,cityid表示城市对应的天气代号,temp1和temp2表示气温是几度到几度,weather表示今日天气信息的描述,img1和img2表示今日天气对应的图片,ptime表示天气发布的时间。
确定了查询地址之后,接下来就调用HttpUtil的sendHttpRequest()方法来向服务器发送请求,响应的数据会回调到onFinish()方法中,然后去调用Utility的handleProvincesResp
28、onse()方法牵扯到了UI操作,因此必须要在主线程中调用,这里借助了runOnUiThread()方法来实现从子线程切换到主线程,它的实现原理其实也是基于异步消息处理机制的。现在数据库中已经存在了数据,因此调用queryProvinces()就会直接将数据显示到界面上。
当你点击了某个省的时候进入到ListView的onClick()方法中,这个时候会根据当前的级别来判断是去调用queryCities()方法还是queryCounties()方法,queryCities()方法是去查询市级数据,而queryCounties()方法是去查询县级数据,这两个方法内部的流程和queryProvi
29、nces()方法基本相同,不重复说明了。
这里重写onBackPressed()方法来覆盖默认Back键的行为,会根据当前的级别来判断是返回市级列表、省级列表、还是直接退出。
配置androidMainifest.xml文件。为活动添加访问网络权限
现在已经程序已经可以运行。如图3.6所示。
图3.6 省份菜单
可以看到,全国所有的省级单位都出来了,继续查看市级单位,比如点击黑龙江如图3.7所示。
图3.7 市级菜单
然后再点击黑河,结果如图3.8
30、所示。
图3.8 县级菜单
这样第二阶段的开发工作完成。
3.2.2.3 第三阶段
1.查询天气,需要把天气信息显示出来。所以要穿件一个新的界面用于显示。创建weather_layout.xml。在这个布局文件中,并没有特殊的控件,基本就是使用TextView显示数据信息,然后嵌套多层LinearLayout和RelativeLayout来控制TextView的显示位置。
在Utility类中添加几个方法,用于解析和处理服务返回的JSON数据,如下所示
其中handleWeatherResponse()方法用于将JSON格式的天气信息全部解析出来,saveWeather
31、Info()方法,用于将这些数据都存储到SharedPreferences文件中。
在activity包下,新建WeatherActivity继承自Activity。该活动用于显示城市名,发布时间,气温,日期等。
最后将ChooseAreaActivity和WeatherActivity关联。在ChooseAreaActivity中添加代码:
SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(this);
if(prefs.getBoolean("city_selected", fals
32、e)&&!isFromWeatherActivity){
Intent intent=new Intent(this,WeatherActivity.class);
startActivity(intent);
finish();
…………
else if(currentLevel==LEVEL_COUNTY){
String countyCode=countyList.get(index).getCountyCode();
Intent intent=new Intent(ChooseAreaActivity.this,W
33、eatherActivity.class);
intent.putExtra("county_code", countyCode);
startActivity(intent);
finish();
最后添加切换城市和更新天气按钮。
运行程序,如图3.9所示
图3.9 北安天气
四、软件测试
4.1 电脑模拟器测试
软件编写完成,对其进行测试,首先将手机天气预报系统在电脑模拟环境下测试的方案,以下是对系统进行测试,在Eclipse 的Package Explorer 窗口中用鼠标右键选择weather工程名,在弹出的窗口中选择"Run A
34、s"→"Android Application"安装该应用程序到Android 模拟器并启动它,如图4.1所示。
图4.1 启动模拟器
等待模拟器启动完成软件的安装,然后单击所要查询的城市名称列表项,稍等片刻便会显示出该城市的天气实况信息。电脑模拟测试结果如图4.2所示。
图4.2 潞城天气
经查询当地的天气发现基本一致,所以可以知道软件获得的数据是正确的,说明软件的可行性基本满足要求。
4.2手机端测试
图 4.3 临武天气
该测试说明开发的天气预报软件可以独立在Android设备运行,满足设计
35、要求的独立性,通过与网络的数据比较,可以发现,天气预报软件获取的数据与实际的天气情况非常接近,说明数据的准确性,实时性;从图9可以看出,当选择不同的城市时,软件可以通过网络获取不同城市的天气信息,说明该天气预报软件可以获取不同城市的气象信息,满足设计要求。
五、总结与展望
5.1总结
为期两周的Android实训已经结束,在这两周中我学习了很多,也得到了很多。实训是把理论与实际结合,通过对理论知识的理解,领悟从而运用到生活实际巩固所学的知识,提高对实际生活的认识,积累经验。在此期间能够初次体会到实际生产中的种种技能与经验。完成一项项项目能体现出独立思考能力。在本次Android天
36、气预报软件开发过程中,我通过学校的图书馆资料和网上查询,了解了Android的搭建、Intent、Activity、Service等知识。Android环境搭建的基本工作完成后,本文对界面的控件、布局和美观方面进行设计,对数据获取和解析做了分析,通过模拟器显示城市名片,实时天气情况
Android生产实习对目前应用现状进行了分析与比较,进而研究分析Android平台的系统架构和组件模型。在此基础上,基于Android平台设计和开发,实现对android编程的理解与应用,特别是在对于控件的应用和活动的创建有了很深的理解。
5.2不足和展望
这次实习我制作的软件还是有很多不足之处,这个软件还
37、可以进一步发展,比如“短信预报”、“新闻模块”等功能,如果这些功能都能实现将会对用户有更多的帮助,使他们的生活更加愉悦和方便。我会继续为这个软件添加功能,直到它成长为一个强大的app。Android 智能手机应用程序的开发涉及了它的整个体系结构,是一项非常复杂的工程。我要以严谨的态度对待它。不骄纵,不菲薄。
参考文献
[1] .Bill Phillips.Android编程权威指南.人民邮电出版社,2015
[2].任玉刚.Android开发艺术探索.电子工业出版社,2016
[3].何红辉 关爱民.Android 源码设计模式解析与实战.人民邮电出版社,2015
[
38、4].郝玉龙 .Android程序设计基础.北京交通大学出版社,2013
[5].徐宜生.Android 群英传.电子工业出版社,2012
附录
重要程序1
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isFromWeatherActivity=getIntent().getBooleanExtra("from_weather_activity", false);
SharedPreferences prefs=
39、PreferenceManager.getDefaultSharedPreferences(this);
if(prefs.getBoolean("city_selected", false)&&!isFromWeatherActivity){
Intent intent=new Intent(this,WeatherActivity.class);
startActivity(intent);
finish();
return ;
}
requestWindowFeature(Window.FEATU
40、RE_NO_TITLE);
setContentView(R.layout.choose_area);
listView=(ListView)findViewById(R.id.list_view);
titleText=(TextView)findViewById(R.id.title_text);
adapter=new ArrayAdapter(this, android.R.layout.simple_list_item_1, dataList);
listView.setAdapter(adapter);
41、 coolWeatherDB=CoolWeatherDB.getInstance(this);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> arg0, View view, int index,long arg3) {
if(currentLevel==LEVEL_PROVINCE){
selectedProvince=provinceList.get(index)
42、
queryCitys();
}else if(currentLevel==LEVEL_CITY){
selectedCity=cityList.get(index);
queryCounty();
}else if(currentLevel==LEVEL_COUNTY){
String countyCode=countyList.get(index).getCountyCode();
Intent intent=new Intent(ChooseAreaActivity.this,WeatherActivity
43、class);
intent.putExtra("county_code", countyCode);
startActivity(intent);
finish();
}
}
});
queryProvince();//加载省级数据
}
//查询全国的省,优先从数据库查询,如果没有查询到再去服务器上查询
private void queryProvince() {
provinceList=coolWeatherDB.loadProvince();
if(provinceList.size(
44、)>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");
45、 }
}
重要程序2
private void initViews() {
weatherInfoLayout=(LinearLayout)findViewById(R.id.weather_info_layout);
cityNameText=(TextView)findViewById(R.id.city_name);
publishText=(TextView)findViewById(R.id.publish_text);
weatherDespText=(TextView)findViewById(R.id.weather_desp);
tem
46、p1Text=(TextView)findViewById(R.id.temp1);
temp2Text=(TextView)findViewById(R.id.temp2);
currentDateText=(TextView)findViewById(R.id.current_date);
switchCity=(Button)findViewById(R.id.switch_city);
refreshWeather=(Button)findViewById(R.id.refresh_weather);
String countyCode=getIntent
47、).getStringExtra("county_code");
if(!TextUtils.isEmpty(countyCode)){//由县级代号去查询天气
publishText.setText("同步中...");
weatherInfoLayout.setVisibility(View.INVISIBLE);
cityNameText.setVisibility(View.INVISIBLE);
queryWeatherCode(countyCode);
}else{//没有县级代号就直接显示本地天气
showWeather();
48、
}
}
private void initEvents() {
switchCity.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(WeatherActivity.this, ChooseAreaActivity.class);
intent.putExtra("from_weather_activity", true);
startActivity(inte
49、nt);
finish();
}
});
refreshWeather.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
publishText.setText("同步中...");
SharedPreferences prefs=PreferenceManager.getDefaultSharedPreferences(WeatherActivity.this);
String weatherCode=prefs.getString("weather_code", "");
if(!TextUtils.isEmpty(weatherCode)){
queryWeatherInf