StarkHuang 2010-03-29
天气Widget : 负责显示 当天 未来几天 天气信息
还是一步步从头说吧:
[代码 步骤]
1. 定义widget所需界面: weatherlayout.xml 包括以下几个View
* ImageView 用于显示:天气图片 * TextView 用于描述天气信息 比如:温度 湿度 紫外线指数 紫外线强度 * ImageView 相当于Button 负责查询明天天气情况
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/text" android:textSize="12px" android:textStyle="bold|italic" android:textColor="#008800" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/next" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
2. 定义 weathersetting.xml 用于定义widget相关各属性
<?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" //widget最小宽度 android:minWidth="246dip" //widget最小高度 android:minHeight="22dip" //刷新率 因为该widget要求从网络获取信息 导致运行时间较长 而该单位偏大 故使其为0 至于数据刷新 通过别的方法 具体见下文 android:updatePeriodMillis="0" //widget使用的布局 android:initialLayout="@layout/weatherlayout" />
3. 定义一些后续用到的字串
public class WeatherColumn { //HK 地区天气查询 相关地址 public final static String WeatherHttpHead = "http://202.140.96.134:8080/FS-RSS/"; public final static String WeatherLocal = WeatherHttpHead + "ftpfile/local_weather.xml"; public final static String WeatherForcast = WeatherHttpHead + "ftpfile/forecast_weather.xml"; //broadcast definition public final static String BroadcastWidget = "BroadcastWidget"; public final static String BroadcastMoniterNext = "BroadcastMoniterNext"; //天气信息数据 比如:哪里 时间 温度 湿度 紫外线指数 紫外线强度 或 所有信息一起 public final static String WeatherLocation = "WeatherLocation"; public final static String WeatherTime = "WeatherTime"; public final static String WeatherTemporary = "WeatherTemporary"; public final static String WeatherHumidity = "WeatherHumidity"; public final static String WeatherUvIndext = "WeatherUvIndext"; public final static String WeatherUvIntensity = "WeatherUvIntensity"; public final static String WeatherIco = "WeatherIco"; //用于标记查询哪天天气 public final static String DayInfo_Widget = "DayInfo_Widget"; public final static String DayInfo_Activity = "DayInfo_Activity"; public final static String DayNow = "DayNow"; public final static String DayNext = "DayNext"; }
4. 定义2 Service 作用:
* WidgetUpdate 用于执行AppWidgetProvider 天气信息显示 及其他事情 * WeatherMoniter 用于执行与天气查询有关事情
5. WidgetUpdate 定义如下:
//负责具体数据查询 显示 public static class WidgetUpdate extends Service { Context context; RemoteViews rView; public void onStart(Intent intent, int startId) { rView = new RemoteViews(getPackageName(), R.layout.weatherlayout); WidgetInfoListenerHelper helper = new WidgetInfoListenerHelper(this); helper.registerAction(WeatherColumn.BroadcastWidget); setViewBroadcastClickListener(rView,R.id.next,WeatherColumn.BroadcastMoniterNext); rView.setTextViewText(R.id.text, "Hi,WeatherWidget!"); rView.setImageViewResource(R.id.next,R.drawable.next); //setViewActivityClickListener(rView, R.id.image, // new Intent(this, WeatherActivity.class)); notifyViewChanged(); } public void notifyViewChanged(){ // Push update for this widget to the home screen ComponentName batteryWidget = new ComponentName(this, WeatherWidget.class); AppWidgetManager manager = AppWidgetManager.getInstance(this); manager.updateAppWidget(batteryWidget, rView); } //refer- startActivity(Intent) public void setViewActivityClickListener(RemoteViews remte,int id,Intent i){ PendingIntent pi = PendingIntent.getActivity(this,1,i,0); remte.setOnClickPendingIntent(id, pi); } //refer- sendBroadcast(Intent) public void setViewBroadcastClickListener(RemoteViews remte,int id,String filter){ Intent i = new Intent(filter); PendingIntent pi = PendingIntent.getBroadcast(this,1,i,0); remte.setOnClickPendingIntent(id, pi); } //显示 天气数据 public void displayText(String s){ rView.setTextViewText(R.id.text, s); notifyViewChanged(); } //显示 天气图片 public void displayImage(Bitmap bp){ rView.setImageViewBitmap(R.id.image,bp); notifyViewChanged(); } //下载 目标地址 网络图片 public Bitmap queryImageByURI(String iu){ try{ URL imgURL = new URL(iu); URLConnection conn = imgURL.openConnection(); conn.connect(); InputStream is = conn.getInputStream(); BufferedInputStream bis = new BufferedInputStream(is); Bitmap bm = BitmapFactory.decodeStream(bis); bis.close(); is.close(); return bm; }catch(Exception e){ return null; } }
6. 在WidgetUpdate 里面定义BroadcastReceiver 用于接收天气数据 并显示之
//负责接收天气数据 public class WidgetInfoListenerHelper extends BroadcastReceiver { Context context; WidgetInfoListenerHelper listener; //construct public WidgetInfoListenerHelper(Context c){ context = c; //to instance it listener = this; } public void registerAction(String action){ IntentFilter filter = new IntentFilter(); filter.addAction(action); context.registerReceiver(listener,filter); } @Override public void onReceive(Context arg0, Intent arg1) { // TODO Auto-generated method stub Bundle b = arg1.getExtras(); if(b.containsKey(WeatherColumn.DayInfo_Widget)){ String string = b.getString(WeatherColumn.DayInfo_Widget); //区分该Bundle是 今天/明天 if(string.equals(WeatherColumn.DayNow)){ //Log.d("TAG","[Widget: retutn Weather - now]"); } else { //Log.d("TAG","[Widget: retutn Weather - forcast]"); } } String weather_info = ""; /* 忽略一些查询到的信息 if(b.containsKey(WeatherColumn.WeatherLocation)){ weather_info += b.getString(WeatherColumn.WeatherLocation); } if(b.containsKey(WeatherColumn.WeatherTime)){ weather_info += b.getString(WeatherColumn.WeatherTime); } */ if(b.containsKey(WeatherColumn.WeatherTemporary)){ weather_info +="温度:"+ b.getString(WeatherColumn.WeatherTemporary); displayText(weather_info); } if(b.containsKey(WeatherColumn.WeatherHumidity)){ weather_info += "\n" + "湿度:"+b.getString(WeatherColumn.WeatherHumidity); displayText(weather_info); } if(b.containsKey(WeatherColumn.WeatherIco)){ Bitmap bmp = queryImageByURI(WeatherColumn.WeatherHttpHead + b.getString(WeatherColumn.WeatherIco)); displayImage(bmp); } } }
7. WeatherMoniter 定义2 Bundle 一个用于存放当天天气数据 一个用于存放明天数据
8. 定义2个用于查询天气的函数 因为这2个*.xml的节点不同 所以需要分别定义
* 查询今天
public void queryWeatherLocal(String s){ try { URL url = new URL(s); URLConnection connection = url.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection)connection; InputStream in = httpConnection.getInputStream(); int responseCode = httpConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbfactory.newDocumentBuilder(); //解析目标 Document dom = db.parse(in); //得到其所有子Element Element docEle = dom.getDocumentElement(); //得到指定的列 NodeList nl = docEle.getElementsByTagName("channel"); if (nl != null && nl.getLength() > 0) { for (int i = 0 ; i < nl.getLength(); i++) { //得到某行数据 Element entry = (Element)nl.item(i); Element info = (Element)entry.getElementsByTagName("item").item(0); //从该行中取出目标 方法:键值 Key-Value Element eTitle = (Element)info.getElementsByTagName("title").item(0); Element eDay = (Element)info.getElementsByTagName("pubDate").item(0); Element eDescription = (Element)info.getElementsByTagName("description").item(0); //取出其内容 String scity = eTitle.getFirstChild().getNodeValue(); String stime = eDay.getFirstChild().getNodeValue(); String sdescription = eDescription.getFirstChild().getNodeValue(); //遍历目标 以指定字符分割 然后按顺序放入String[] String[] string = sdescription.split("<br>"); String temporary = string[0]; String temp = temporary.split("=")[1]; String humidity = string[1]; String hum = humidity.split("=")[1]; String uIndex = string[2]; String uv_Index = uIndex.split("=")[1]; String uIntensity = string[3]; String uv_Intensity = uIntensity.split("=")[1]; //String uIntensity = string[3]; String icoName = string[5]; String address = icoName.split(" ")[1]; String address1 = address.split("=")[1]; //去除两边的"\"" String address2 = address1.replaceAll("\"", ""); //Log.d("TAG","location:"+scity); //Log.d("TAG","time:"+stime); //Log.d("TAG","temporary:"+temp); //Log.d("TAG","humidity:"+hum); //Log.d("TAG","uv index:"+uv_Index); //Log.d("TAG","uv intension:"+uv_Intensity); //image.setImageBitmap(queryImageByURI(ico_preface+address2)); //Bundle bundle = new Bundle(); wLocal.clear(); wLocal.putString(WeatherColumn.WeatherLocation, scity); wLocal.putString(WeatherColumn.WeatherTime, stime); wLocal.putString(WeatherColumn.WeatherTemporary, temp); wLocal.putString(WeatherColumn.WeatherHumidity, hum); wLocal.putString(WeatherColumn.WeatherUvIndext, uv_Index); wLocal.putString(WeatherColumn.WeatherUvIntensity, uv_Intensity); wLocal.putString(WeatherColumn.WeatherIco, address2); //标记此Bundle为 local_weather 数据 wLocal.putString(WeatherColumn.DayInfo_Widget, WeatherColumn.DayNow); } } } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (ParserConfigurationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }catch (SAXException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } }
如何调用:
String local = WeatherColumn.WeatherLocal; queryWeatherLocal(local);
* 查询明天 因为目标会提供后4天天气信息 所以只选择第一天 并显示之
public void queryWeatherForcast(String s){ try { URL url = new URL(s); URLConnection connection = url.openConnection(); HttpURLConnection httpConnection = (HttpURLConnection)connection; InputStream in = httpConnection.getInputStream(); int responseCode = httpConnection.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbfactory.newDocumentBuilder(); //解析目标 Document dom = db.parse(in); /* 步骤: * 1. 得到解析后目标:Document dom * 2. 在Document基础上做getDocumentElement()以得到最基本处理单位:Element e * 3. 再在Element基础上做getElementsByTagName() */ Element docEle = dom.getDocumentElement(); //得到指定的列 NodeList item = (NodeList)docEle.getElementsByTagName("item"); if (item != null && item.getLength() > 0) { for (int i = 0 ; i < item.getLength(); i++) { Element entry = (Element)item.item(0); //从该行中取出目标 方法:键值 Key-Value //取出 <title>....</title> Element eTitle = (Element)entry.getElementsByTagName("title").item(0); String title = eTitle.getFirstChild().getNodeValue(); //取出 <pubDate>....</pubDate> Element eData = (Element)entry.getElementsByTagName("title").item(0); String data = eTitle.getFirstChild().getNodeValue(); //取出 <description>....</description> Element eDescription = (Element)entry.getElementsByTagName("description").item(0); String description = eDescription.getFirstChild().getNodeValue(); //一次记录所有天气数据 以"\n"代替<br> //String all = description.replace("<br>", "\n"); //wForcast.putString(WeatherColumn.WeatherAll, all); String[] string = description.split("<br>"); String lTemp = string[0].split("=")[1]; String hTemp = string[1].split("=")[1]; String lHumidity = string[2].split("=")[1]; String hHumidity = string[3].split("=")[1]; String icoName = string[5]; String address = icoName.split(" ")[1]; String address1 = address.split("=")[1]; //去除两边的"\"" String ico = address1.replaceAll("\"", ""); wForcast.clear(); wForcast.putString(WeatherColumn.WeatherTemporary, lTemp+"-"+hTemp); wForcast.putString(WeatherColumn.WeatherHumidity, lHumidity+"-"+hHumidity); wForcast.putString(WeatherColumn.WeatherIco, ico); } } } } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (ParserConfigurationException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }catch (SAXException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } }
如何调用:
String forcast = WeatherColumn.WeatherForcast; queryWeatherForcast(forcast);
9. 何时查询 查询哪天
1. WeatherMoniter 启动以后 会查询当天天气 并把查询结果返回给WeatherWidget 由其负责显示 2. 收到WeatherWidget的命令后 查询明天天气 并返回结果
10. 最上面提到的数据刷新问题 我的办法:开辟Thread 计时 时间到就重新查询数据 并返回结果给WeatherWidget 与WeatherMoniter 一同启动之
//创建Thread 用于计时 public void startLoop(){ Thread loop = new Thread(new TimeCounter()); loop.start(); } public class TimeCounter implements Runnable { @Override public void run() { // TODO Auto-generated method stub while(true){ loop(100000000); Log.d("TAG","[WeatherMoniter: Time to refresh!]"); //刷新天气数据 requeryWeatherInfo(); sendWeatherInfo(WeatherColumn.BroadcastWidget,wLocal); } } public void loop(long i){ long l = i; while(l>0){ l--; } } }
11. emulator 运行截图 对了 别忘记权限
* 当天:
* 明天:
结束!