安卓天气预报报告
基于Android平台的天气预报系统的设计与开发
目录
1.前言...............................................................
1.1 系统开发的目的 ..............................................
1.2 项目需求分析 ................................................ .天气预报程序设计................................................... 2
2.1 Android的UI界面的实现.......................................
2.2欢迎界面的实现................................................
2.3 主界面功能实现 ...............................................
2.4数据存储...................................................... 3.结束语.............................................................
?
1.前言
1.1 系统开发的目的
由于智能手机的快速普及~智能操作系统在手机市场占据了很大一部分~其中以Andriod和iOS最为流行。为了让智能手机用户能够随时随地查询互联网所提供的信息~一种非常高效的
就是将网络系统的功能扩展到智能手机终端上~让手机能够通过移动网络访问Web网站并处理各种各样的业务。由于在大学学习过java语言~并且Andriod系统是基于java语言开发的~因此想开发一套基于Andriod平台的3G手机气象软件~并从网上获取天气预报信息。Android平台的天气预报软件的开发可以进一步扩大气象信息的覆盖面~让广大Android手机用户能够在第一时间获取最新的天气预报信息~以便提前预防~方便出行。同时~把气象灾害造成的损失降到最低~也可以提高公共服务质量~更好的发挥气象事业对经济社会发展的现实性作用~有巨大的实用价值。虽然该技术在Android平台已经比较成熟~但是通过该软件的开发仍然能帮助我更好的认识Android系统的工作原理。
1.2 项目需求分析
(1) 系统能够通过手机精确定位全国各个城市未来几天的天气情况
(2) 系统具有安全性~独立性~可靠性
(3) 系统界面设计优美~符合用户的操作习惯
2.天气预报程序设计
2.1 Android的UI界面的实现
在Android中用户界面都是布局在XML文件中的。这些XML文件放在工程/res/layout下面。这对以前熟悉java可视化编程的人来说可能有些不太习惯。可能有人疑问为什么要把布局文件放在XML文件里呢,其实有过web基础的都应该知道mvc框架~这是一种用于网站开发的非常优秀的思想~在安卓系统中~
1
安卓开发人员把用户界面放在XML文档中定义~这样就可以让XML文档专门负责用户UI界面设置~而java程序专门负责业务实现~从而降低程序的耦合~便于程序的维护和扩展~符合mvc设计思想。
在XML中Android开发人员放置了很多我们经常用到的一些组件~比如view~Button~EditText等~通过这些系统提供的控件~我们可以开发出我们想要的程序界面来。
Layout 是一类特殊的ViewGroup 控件~它们本身并不显示任何的内容~存在的惟一原因就是其中的内部结构~能够更好地摆放它的子控件。比如Linearlayout,线性布局,,可将子控件按水平或垂直方向按顺序排列下去,Tablelayout,
格布局,,可以将子控件按照表格的形式~一枚枚放置好,Relativelayout,相对布局,~实际应用中这个布局更灵活~它可以设定各个控件之间的对齐和排列关系~适合编写复杂的界面。有了Layout 的存在~控件和控件之间不再是独立地存在~而是更有机地结合在一起~设定起来也更为方便。
在本程序中布局文件我一共用到了如图4.1所示的布局文件
图 4.1 布局文件图
其中city_detail用来显示城市简介的信息~city_environment用来显示当前所在城市的生活指数信息,city_layout用来显示用来选择城市布局~content_layout用来显示天气情况~gps_view用来显示gps定位信息~login用来显示登录欢迎界面~main包含了用来切换的三个界面~tqyb显示主界面布局
2.2欢迎界面的实现
考虑到程序界面的美观效果~在进入主界面之前我首先设置了一个用来跳转的Activity~并命名为LoginActivity。在LoginActivity中显示login布局
Timer timer = new Timer();
final Intent intent = new Intent(this,edu.njue.app.WeatherWebServiceActivity.class);
TimerTask task = new TimerTask(){
@Override
public void run() {
startActivity(intent);
LoginActivity.this.finish();
}
};
timer.schedule(task, 1000);
}
2.3 主界面功能实现
第一次安装好进入程序界面时~系统会显示“本程序是第一次运行~请选择想要了解的城市”的窗口。在这里我把默认城市设置为南京~如果用户不单击城市选择按钮的时候~则会显示的是南京近两天的天气情况。如图4.3所示。第一次运行时~我是通过在手机文件系统中判断是否存在一个名为shared_prefs的文件来判断是否为第一次运行~如果存在则不是第一次运行~系统会继续执行向下执行代码~如果系统中不存在该文件~则为第一次运行该程序~程序弹出对话框告诉用户。
在主程序界面~我运用的主要是ViewFlipper组件~通过该组件可以实现滑 动显示界面~不过在设置滑动界面之前我们要先设定滑动的动画。其中left_in
中的代码为:
3
当我们定义了滑动效果之后还要在main.xml中把想要滑动的布局包含进去。
"
有了这个布局之后我们就可以在主程序界面调用界面进行切换了。主程序实现触屏监听接口~监听触摸时的x,y坐标~以及当手指离开时的坐标~如果两者之间的距离大于或小于100像素~则认为想要实现滑动。
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
touchDownX = event.getX();
return true;
}else if(event.getAction()==MotionEvent.ACTION_UP){
touchUpX = event.getX();
if(touchUpX-touchDownX>100){//从左往右滑动
viewFlipper.setAnimation(AnimationUtils.loadAnimation(this,android.R.anim.slide_in_left));
viewFlipper.setAnimation(AnimationUtils.loadAnimation(this,android.R.anim.slide_out_rig
ht));
viewFlipper.showPrevious();
}else if(touchDownX-touchUpX>100){
viewFlipper.setAnimation(AnimationUtils.loadAnimation(this, R.anim.right_in));
viewFlipper.setAnimation(AnimationUtils.loadAnimation(this,R.anim.left_out));
viewFlipper.showNext();
}
return true;
}
return false;
}
获取天气信息并显示
天气预报系统最重要的当然是如何才能有效的获取天气信息了~要想获取实时的天气信息~我们就要访问专门提供天气信息的网站把网站返回的信息解析出想要的信息并显示在手机上。通过网上的搜索~我找到了一个提供webservice的网站:通过向这个网站提供参数就可以获取指定城市的天气信息了~该网站是从中央气象台接收的信息~数据准确可靠。每隔两个小时更新一次天气情况~具有很好的实时性。
Webservice的出现成功的解决了不同平台和语言之间进行数据交换的问题~它主要使用XML文档来传递消息~并将该消息发送给任何请求对象~由于不同系统对XML的完美支持~因此从根本上完善了跨平台服务~任何时刻、任何平台我们可以使用任何语言来访问该Webservice服务。对于Webservice使用者而言~webservice API与任何操作平台无关~它只是以XML语言作为数据交换格式。
Webservice平台主要涉及到的技术是SOAP,Simple Object Access Protocol,~WSDL,Web Service Description Language,
SOAP依赖于XML文档来构建~一条SOAP消息就是一份特定的XML文档~SOAP主要包含三个方面的主要元素:
(1)
根元素~该元素是必填的。其中SOAP消息用来传递的数
据的XML文档以该元素作为根元素
(2)
元素~该元素是选填的。它包含了SOAP消息的头信息
(3)
元素~该元素是必填的。它包含所有的调用和相应的内容
Java本身提供了很丰富的Web Service支持~比如Sun公司制定的JAX-WS 2规范~Apache开源组织提供的Axis1,Axis2,CXF等。我们可以使用Google公司为Android平台开发的专门处理Webservice信息的ksoap2-android进行客户端的
5
开发。使用该第三方包的方法为:
1.创建HttpTrasportSE对象
2.创建SoapSerializationEnvelope对象
3.创建SoapObject对象~创建该对象时需要传入所要调用Web Service的命名空间
4.如果有参数需要传给WebService服务器端~调用SoapObject对象的addProperty,String name,Object value)方法来设置参数~该方法的name参数指定参数名~value指定参数值
5.调用SoapSerializationEnvelope的setOutputSoapObject()方法~或者直接对bodyout属性赋值~将前两步创建的SoapObject对象设 SoapSerializationEnvelope
的传出SOAP消息体
6.调用对象的call()方法~并以SoapSerializationEnvelope作为参数调用远程的WebService
调用完成后~访问SoapSerializationEnvelope对象的bodyin属性~该属性7.
返回一个SoapObject对象~该对象就代表了Web Service的返回消息~解析该SoapObject对象~即可获取调用Web Service的返回值。
基于此编写调用webservice服务的类WebServiceUtil
返回城市列表
public static List
getProvinceList()
{
// 需要调用的方法名(获得本天气预报Web Services支持的洲、国内外省份和城市信息)
//String methodName = "getRegionProvince";
String methodName = "getSupportProvince";
// 创建HttpTransportSE传输对象
HttpTransportSE httpTranstation = new HttpTransportSE(SERVICE_URL);
httpTranstation.debug = true;
// 使用SOAP1.1协议创建Envelop对象
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
// 实例化SoapObject对象
SoapObject soapObject = new SoapObject(SERVICE_NS, methodName);
envelope.bodyOut = soapObject;
// 设置与.Net提供的Web Service保持较好的兼容性
envelope.dotNet = true;
try
{
// 调用Web Service
httpTranstation.call(SERVICE_NS + methodName, envelope);
if (envelope.getResponse() != null)
{
// 获取服务器响应返回的SOAP消息
SoapObject result = (SoapObject) envelope.bodyIn;
SoapObject detail = (SoapObject) result.getProperty(methodName
+ "Result");
// 解析服务器响应的SOAP消息。
return parseProvinceOrCity(detail);
}
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
解析出省份和城市~把返回的数据用split方法切割~只取需要的省份数据 private static List parseProvinceOrCity(SoapObject detail)
{
ArrayList result = new ArrayList();
for (int i = 0; i < detail.getPropertyCount(); i++)
{
String str = detail.getProperty(i).toString();
// 解析出每个省份
result.add(str.split(" ")[0]);
}
return result;
}
通过具体的城市获取天气信息
public static SoapObject getWeatherByCity(String cityName)
{
// 根据城市或地区名称查询获得未来三天内天气情况、现在的天气实况、天气和
7
生活指数
//String methodName = "getWeather";
String methodName = "getWeatherbyCityName";
HttpTransportSE httpTranstation = new HttpTransportSE(SERVICE_URL);
httpTranstation.debug = true;
SoapSerializationEnvelope envelope = new
SoapSerializationEnvelope(SoapEnvelope.VER11);
SoapObject soapObject = new SoapObject(SERVICE_NS, methodName);
//soapObject.addProperty("theCityCode", cityName);
soapObject.addProperty("theCityName",cityName);
envelope.bodyOut = soapObject;
envelope.dotNet = true;
try
{
// 调用Web Service
httpTranstation.call(SERVICE_NS + methodName, envelope);
if (envelope.getResponse() != null)
{
// 获取服务器响应返回的SOAP消息
SoapObject result = (SoapObject) envelope.bodyIn;
SoapObject detail = (SoapObject) result.getProperty(methodName+ "Result");
// 解析服务器响应的SOAP消息。
return detail;
}
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
接下来我们只需要对该文件解析就可以拿出我们需要的数据了。然后把对应的数
据添加在相应的地方即可。
城市列表的选择:
当我们单击城市选择按钮的时候会跳出城市选择界面对应的方法为
public void show_dialog(int cityId)
{
city_text = (TextView)findViewById(R.id.city_text);
switch (cityId)
{
case CITY:
// 取得city_layout.xml中的视图
final View view = LayoutInflater.from(this).inflate(
R.layout.city_layout, null);
// 省份Spinner
province_spinner = (Spinner) view.findViewById(R.id.province_spinner);
// 城市Spinner
city_spinner = (Spinner) view.findViewById(R.id.city_spinner);
provinces = WebServiceUtil.getProvinceList();
ArrayAdapter adapter = new
ArrayAdapter(this,android.R.layout.simple_spinner_item, provinces);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
province_spinner.setAdapter(adapter);
// 省份Spinner监听器
province_spinner.setOnItemSelectedListener(new OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView> view,
View parent, int position, long id)
{
citys =
WebServiceUtil.getCityListByProvince(provinces.get(position)); ArrayAdapter adapter1 = new ArrayAdapter(WeatherWebServiceActivity.this, android.R.layout.simple_spinner_item, citys);
adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
city_spinner.setAdapter(adapter1);
}
@Override
public void onNothingSelected(AdapterView> arg0)
{
}
});
// 城市Spinner监听器
city_spinner.setOnItemSelectedListener(new OnItemSelectedListener()
{
@Override
public void onItemSelected(AdapterView> arg0, View arg1,
int position, long arg3)
{
city_str = citys.get(position);
9
}
@Override
public void onNothingSelected(AdapterView> arg0)
{
}
});
// 选择城市对话框
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setTitle("请选择所属城市");
dialog.setView(view);
dialog.setPositiveButton("确定",new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
city_text.setText(city_str);
writeSharedPreference(city_str);
refresh(city_str);
checkWeather(city_str);
}
});
dialog.setNegativeButton("取消",new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
}
});
dialog.show();
break;
default:
break;
}
}
编写gps定位转换类用于定位当前位置信息ConverUtil。由于Google SDK内部的一个小bug~我们不能直接利用手机内部来定位地址~必须要把当前的经纬度
信息发送给谷歌网站来解析。
public static String getAddress(double longitude,double latitude){
//
HttpClient client = new DefaultHttpClient();
//Get方法得到内容
HttpGet httpGet = new HttpGet(";
+ "geocode/json?latlng="
+ latitude + "," + longitude
+ "&sensor=false®ion=cn");
StringBuilder sb = new StringBuilder();
try{
HttpResponse response = client.execute(httpGet);
HttpEntity entity = response.getEntity();
//获取服务器响应的字符串
InputStream is = entity.getContent();
int b;
while((b = is.read())!=-1){
sb.append((char)b);
}
//把服务器相应的字符串转化为JSon对象
JSONObject jsonObj = new JSONObject(sb.toString());
//解析出相应结果中的地址信息
String s1 =
jsonObj.getJSONArray("results").getJSONObject(0).getString("formatted_address");
String s2 = s1.split(",")[3];
Log.i("start", s2);
return
jsonObj.getJSONArray("results").getJSONObject(0).getString("formatted_address");
}catch(Exception e){
e.printStackTrace();
}
return null;
}
本软件同样实现了传感器接口~可以实时监测当前外部温度~增加用户的友好体验~主要方法为:
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
float[] values = event.values;
//真机上获取触发event的传感器类型
//int sensorType = event.sensor.getType();
//模拟器上获取触发event的传感器类型
int sensorType = event.type;
StringBuilder sb = null;
11
switch(sensorType){
case Sensor.TYPE_TEMPERATURE:
sb = new StringBuilder();
sb.append(values[0]);
sb.append("?");
et.setText(sb.toString());
break;
default:break;
}
}
2.4数据存储
有时候应用程序需要有数据保存~而且这些数据都比较简单~基本上只是一些简单的字符串~标量类型的值等。对于这种数据~Android提供了SharedPreferences进行保存。SharedPreferences主要保存的是一些简单的key-value对。在本程序中~我首先设置了一个判断网络状态连通情况的方法~返回一个Boolean类型的值~如果网络正常连接~则会从网络刷新天气~如果当前网络状态不好~则会从本地保存的XML文件中读取缓存的天气~方便用户进行查看。
判断网络状态的方法:
/ /判断网络类型是否为3G网络
public boolean isNetwork(){
ConnectivityManager connectivityManager = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo!=null){
return networkInfo.isAvailable();
}
return false;
}
//判断是否为WIFI连接
public boolean isWIFINetwork(){
ConnectivityManager connectivityManager = (ConnectivityManager)this.getSystemService(Context.CONNECTIVITY_SERVICE);
if(connectivityManager == null){
return false;
}else{
NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
if(info!=null){
for(int i=0;i