天气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 运行截图 对了 别忘记权限
* 当天:
* 明天:
结束!