Android 开发一个天气预报桌面小部件

一 本片文章主要记录了如何开发一个桌面小部件,以及如何获取天气预报信息,并实时异步更新界面。

一个典型的桌面小部件开发步骤可以参考:http://www.cnblogs.com/playing/archive/2011/04/12/2013386.html

二 开发步骤

1.创建res/layout/weather_widget_layout.xml用以描述部件的布局,代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/weather_widget_back">
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/city_name"
        android:text="城市名称"
        android:textSize="20sp"
        android:layout_alignParentLeft="true"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="15dp"/>
    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/cloud"
        android:text="风向"
        android:textSize="20sp"
        android:layout_marginTop="10dp"
        android:layout_marginRight="15dp"
        android:layout_alignParentRight="true"/>
    <LinearLayout 
        android:layout_below="@id/city_name"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:layout_marginBottom="10dp">
        <ImageView 
	        android:id="@+id/weather"
	        android:layout_width="0dp"
	        android:layout_height="match_parent"
	        android:src="@drawable/little_rain"
	        android:layout_weight="1"/>
	    <TextView 
	        android:id="@+id/cur_temp"
	        android:layout_width="0dp"
	        android:layout_height="match_parent"
	        android:layout_weight="1"
	        android:gravity="center"
	        android:text="20"
	        android:textSize="50sp"/>
	    <LinearLayout 
	        android:layout_height="match_parent"
	        android:layout_width="0dp"
	        android:layout_weight="1"
	        android:gravity="center"
	        android:orientation="vertical">
	        <TextView 
	            android:id="@+id/low_temp"
	            android:layout_width="match_parent"
	            android:layout_height="wrap_content"
	            android:text="最低  13"
	            android:textSize="30sp"/>
	        <TextView 
	            android:id="@+id/high_temp"
	            android:layout_width="match_parent"
	            android:layout_height="wrap_content"
	            android:text="最高  20"
	            android:textSize="30sp"/>
	    </LinearLayout>
    </LinearLayout>
    
</RelativeLayout> 


2.创建一个小部件的内容提供者文件res/xml/weather_info.xml,代码如下:

 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" 
 	android:minWidth="214dp" 
 	android:minHeight="142dp" 
 	android:updatePeriodMillis="1000" 
 	android:initialLayout="@layout/weather_widget_layout" > 
 </appwidget-provider>


3.创建一个AppWidgetProvider的子类WeatherWidget,代码如下:


public class WeatherWidget extends AppWidgetProvider {
	public static WeatherFm wf = new WeatherFm();

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {
		super.onUpdate(context, appWidgetManager, appWidgetIds);
		Log.v("totoro","totoro1:" + appWidgetIds.length);
		LoadWeatherService.appWidgetIds = appWidgetIds;
		Log.v("totoro","totoro2:" + LoadWeatherService.appWidgetIds.length);
		context.startService(new Intent(context,LoadWeatherService.class));
	}
	
	public static RemoteViews updateRemoteViews(Context context) {
		RemoteViews view = new RemoteViews(context.getPackageName(),R.layout.weather_widget_layout);
		if (null == wf) {
			return null;
		} else {
			view.setTextViewText(R.id.cur_temp, wf.getCurTemperature());
			view.setTextViewText(R.id.low_temp, "低" + wf.getLowestTemperature());
			view.setTextViewText(R.id.high_temp, "高" + wf.getHighestTemperature());
			return view;
		}	
	}

}

关于类中的逻辑,稍后解释。

4. 在AndroidManifest.xml中注册:

<receiver
            android:name="com.monde.mondewidget.weatherwidget.WeatherWidget" 
			android:icon="@drawable/ic_weather_widget"> 
			<intent-filter> 
				<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/> 
			</intent-filter> 
			<meta-data
			    android:name="android.appwidget.provider" 
			    android:resource="@xml/weather_info"/> 
</receiver> 

以上是这个天气预报widget的基本创建步骤,而要实现天气的实时更新,还需要做更多的工作。


     

这是这个桌面部件所要用到的所有类,由于涉及到网络访问,所以在WeatherWidget类的onReceive方法中,无法直接对UI进行更新。

context.startService(new Intent(context,LoadWeatherService.class));这句话是用以启动网络访问获取天气信息的服务,这个类代码如下:


public class LoadWeatherService extends Service implements Runnable{
	private static Object isLock = new Object();
	private static boolean isThreadRun = false;
	public static int[] appWidgetIds;

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.v("onStartCommand","totoro:" + intent.toString());
		new Thread(this).start();
//		synchronized (isLock) {
//			if (!isThreadRun) {
//				isThreadRun = true;
//				new Thread(this).start();
//			}
//		}
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void run() {
		Looper.prepare();
		Log.v("onStartCommand","totoro");
		
		BDLocationUtils utils = new BDLocationUtils(this.getApplicationContext());
		utils.requestBDLocation();
		
		AppWidgetManager manager = AppWidgetManager.getInstance(this);
		WeatherQueryImpl impl = new WeatherQueryImpl(this);
		WeatherWidget.wf = impl.weatherQuery(utils.cityCode);
		RemoteViews view = WeatherWidget.updateRemoteViews(this);
		if (null != view) {
			manager.updateAppWidget(appWidgetIds, view);
		} else {
			Log.e("run", "更新失败");
		}
		
		stopSelf();
		Looper.loop();
	}

}




在这个服务中,启动了一个线程去处理网络访问,获取天气信息并使用返回的数据,更新了UI,实际的天气预报获取操作,均在WeatherQueryImpl中实现:

public class WeatherQueryImpl implements WeatherQuery {
	private Context context;
	
	public WeatherQueryImpl(Context context) {
		this.context = context;
	}

	@Override
	public WeatherFm weatherQuery(String cityCode) {
		Log.v("weatherQuery","totoro:" + cityCode);
		String URL1 = "http://www.weather.com.cn/data/sk/" + cityCode + ".html";
		String URL2 = "http://www.weather.com.cn/data/cityinfo/" + cityCode + ".html";
		WeatherFm wf = new WeatherFm();
		wf.setCityCode(cityCode);
		String Weather_Result = "";
		HttpGet httpRequest = new HttpGet(URL1);
		// 获得当前温度
		try {
			HttpClient httpClient = new DefaultHttpClient();
			HttpResponse httpResponse = httpClient.execute(httpRequest);
			if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				// 取得返回的数据
				Weather_Result = EntityUtils.toString(httpResponse.getEntity());
				Log.v("totoro",Weather_Result);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		//以下是对返回JSON数据的解析
		if(null != Weather_Result&&!"".equals(Weather_Result)){
			try {
				JSONObject JO = new JSONObject(Weather_Result).getJSONObject("weatherinfo");
				wf.setCurTemperature(JO.getString("temp"));
				wf.setCityName(JO.getString("city"));
			} catch (JSONException e) {
				e.printStackTrace();
				return null;
			}
		}
		
		Weather_Result = "";
		httpRequest = new HttpGet(URL2);
		// 获得HttpResponse对象
		try {
			HttpClient httpClient = new DefaultHttpClient();
			HttpResponse httpResponse = httpClient.execute(httpRequest);
			if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				// 取得返回的数据
				Weather_Result = EntityUtils.toString(httpResponse.getEntity());
				Log.v("totoro",Weather_Result);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
		//以下是对返回JSON数据的解析
		if(null != Weather_Result&&!"".equals(Weather_Result)){
			try {
				JSONObject JO = new JSONObject(Weather_Result).getJSONObject("weatherinfo");
				wf.setWeatherCondition(JO.getString("weather"));
				wf.setLowestTemperature(JO.getString("temp2"));
				wf.setHighestTemperature(JO.getString("temp1"));
			} catch (JSONException e) {
				e.printStackTrace();
				return null;
			}
		}
		return wf;
	}

	@Override
	public String cityQuery() {
		try {
			return LocationUtils.getCNByGPSlocation(context);
//			return LocationUtils.getCNByWIFILocation(context);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public WeatherFm getLocalWeather () {
		return weatherQuery(LocationCode.CHINESE_LOCAL_CODE.get(cityQuery()));
	}
}


关于天气预报的获取,来自中国天气网:http://www.weather.com.cn/,比较权威;此前共有3个接口可供使用:

http://m.weather.com.cn/data/101280601.html

http://www.weather.com.cn/data/sk/101280601.html 

http://www.weather.com.cn/data/cityinfo/101010100.html  

而第一个接口现在已经停止更新,第二个接口返回的数据格式如下:

"weatherinfo": {  
        "city": "深圳",  
        "cityid": "101280601",  
        "temp": "31",  
        "WD": "东南风",  
        "WS": "3级",  
        "SD": "58%",  
        "WSE": "3",  
        "time": "17:10",  
        "isRadar": "1",  
        "Radar": "JC_RADAR_AZ9755_JB"  
}  

第三个接口返回的数据格式如下:

"weatherinfo": {  
        "city": "深圳",  
        "cityid": "101280601",  
        "temp1": "26℃",  
        "temp2": "32℃",  
        "weather": "多云",  
        "img1": "n1.gif",  
        "img2": "d1.gif",  
        "ptime": "18:00"  
 }  


都是json格式,处理起来都很方便,而关于城市代码的获取,网上的资料很多。

location相关的类都是与城市定位有关系,由于还未实现完全,就不介绍了。



  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值