相信墨迹天气,大家都见过,他在时间显示和天气界面上,很吸引人,今天我就来模仿一下墨迹天气的桌面组件,但是由于谷歌在天朝频频被墙的缘故,所以我在今天测试的时候,解析xml文件的网页打不开,所以天气显示出了点问题,希望大家能理解,谢谢。(今天9月24日修改为解析中国天气网获取天气了,而且修改组件在桌面居中显示)。

        老规矩,先分享源代码:http://download.csdn.net/detail/weidi1989/4597809

        2013年07月06日更新版本:http://download.csdn.net/detail/weidi1989/5712051

        好了,废话不多说,先上效果图:

  

再来看一下整个小项目的主体结构:

 

首先先声明一个桌面布局的xml文件,即app.xml:

[html]  view plain  copy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <!--  
  3.          指定该桌面组件的基本配置信息:  
  4.     initialLayout:初始时显示的布局  
  5.     minWidth:桌面小控件的最小高度。  
  6.     minWidth:桌面小控件的最小宽度。  
  7.     updatePeriodMillis:更新频率  
  8. -->  
  9. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  
  10.     android:initialLayout="@layout/main"  
  11.     android:minHeight="150dp"  
  12.     android:minWidth="200dp"  
  13.     android:updatePeriodMillis="0" >  
  14.   
  15. </appwidget-provider>  


然后要在manifest文件中声明:

[html]  view plain  copy
 
  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     package="com.way.apptest"  
  3.     android:versionCode="1"  
  4.     android:versionName="1.0" >  
  5.   
  6.     <uses-sdk  
  7.         android:minSdkVersion="8"  
  8.         android:targetSdkVersion="15" />  
  9.   
  10.     <uses-permission android:name="android.permission.INTERNET" />  
  11.   
  12.     <application  
  13.         android:icon="@drawable/ic_launcher"  
  14.         android:label="@string/app_name"  
  15.         android:theme="@style/AppTheme" >  
  16.         <activity android:name="com.way.apptest.MainActivity" >  
  17.         </activity>  
  18.   
  19.         <receiver android:name="com.way.apptest.App" >  
  20.             <!-- 指定桌面小控件的meta-data -->  
  21.             <meta-data  
  22.                 android:name="android.appwidget.provider"  
  23.                 android:resource="@xml/app" />  
  24.             <!-- 将该BroadcastReceiver当成桌面小控件 -->  
  25.             <intent-filter>  
  26.                 <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  
  27.             </intent-filter>  
  28.         </receiver>  
  29.   
  30.         <service android:name=".UpdateService" >  
  31.         </service>  
  32.     </application>  
  33.   
  34. </manifest>  

主要代码:

一:定义一个App类继承AppWidgetProvider,然后再onUpdate方法中启动一个服务去更新时间和天气。

[java]  view plain  copy
 
  1. /** 
  2.  * @author way 
  3.  */  
  4. public class App extends AppWidgetProvider {  
  5.     private Intent intent;  
  6.   
  7.     @Override  
  8.     public void onUpdate(Context context, AppWidgetManager appWidgetManager,  
  9.             int[] appWidgetIds) {  
  10.         intent = new Intent(context, UpdateService.class);  
  11.         context.startService(intent);  
  12.         super.onUpdate(context, appWidgetManager, appWidgetIds);  
  13.     }  
  14.   
  15.     @Override  
  16.     public void onDeleted(Context context, int[] appWidgetIds) {  
  17.         context.stopService(intent);  
  18.         super.onDeleted(context, appWidgetIds);  
  19.     }  
  20. }  


二:本项目中最重要的部分,在这个服务中,我们注册一个广播接收者去接受系统每分钟时间时间变化的广播,从而来更新桌面时间,这样更省电哦,需要注意的是:这个广播接收者必须在代码中注册,在Manifest文件中注册是没有效果的。更新天气,我是通过定义一个定时器,由用户设置时间间隔来更新天气信息。

[java]  view plain  copy
 
  1. package com.way.apptest;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import java.util.Timer;  
  6. import java.util.TimerTask;  
  7.   
  8. import android.app.AlertDialog;  
  9. import android.app.PendingIntent;  
  10. import android.app.Service;  
  11. import android.appwidget.AppWidgetManager;  
  12. import android.content.BroadcastReceiver;  
  13. import android.content.ComponentName;  
  14. import android.content.Context;  
  15. import android.content.DialogInterface;  
  16. import android.content.Intent;  
  17. import android.content.IntentFilter;  
  18. import android.net.ConnectivityManager;  
  19. import android.net.NetworkInfo;  
  20. import android.os.Handler;  
  21. import android.os.IBinder;  
  22. import android.os.Message;  
  23. import android.widget.RemoteViews;  
  24.   
  25. import com.way.getWeather.MyWeather;  
  26.   
  27. /** 
  28.  * @author way 
  29.  */  
  30. public class UpdateService extends Service {  
  31.     private static final int UPDATE = 0x123;  
  32.     private RemoteViews remoteViews;  
  33.     // 数字时间图片资源数组  
  34.     private int[] imgs = { R.drawable.n0, R.drawable.n1, R.drawable.n2,  
  35.             R.drawable.n3, R.drawable.n4, R.drawable.n5, R.drawable.n6,  
  36.             R.drawable.n7, R.drawable.n8, R.drawable.n9, };  
  37.     // 将显示小时、分钟的ImageView定义成数组  
  38.     private int[] dateViews = { R.id.h1, R.id.h2, R.id.m1, R.id.m2 };  
  39.     // 按照中国天气网的天气图片顺序排列好本地资源图片,我这里是随意的~嘿嘿  
  40.     private int[] weatherImg = { R.drawable.sunny, R.drawable.cloudy,  
  41.             R.drawable.chance_of_rain, R.drawable.chance_of_sleet,  
  42.             R.drawable.chance_of_snow, R.drawable.chance_of_storm,  
  43.             R.drawable.clock1, R.drawable.fog, R.drawable.haze,  
  44.             R.drawable.mist, R.drawable.mostly_sunny, R.drawable.mostly_cloudy,  
  45.             R.drawable.lower, R.drawable.middle };  
  46.     private Handler handler = new Handler() {  
  47.   
  48.         @Override  
  49.         public void handleMessage(Message msg) {  
  50.             switch (msg.what) {  
  51.             case UPDATE:  
  52.                 // 更新天气  
  53.                 updateTime();  
  54.                 updateWeather();  
  55.                 break;  
  56.             }  
  57.         }  
  58.     };  
  59.     // 广播接收者去接收系统每分钟的提示广播,来更新时间  
  60.     private BroadcastReceiver mTimePickerBroadcast = new BroadcastReceiver() {  
  61.   
  62.         @Override  
  63.         public void onReceive(Context context, Intent intent) {  
  64.             updateTime();  
  65.         }  
  66.     };  
  67.   
  68.     private void updateWeather() {  
  69.         // Weather w = new GetWeather().googleWeather();  
  70.         // if (w != null) {  
  71.         // System.out.println("当前天气:" + w.getWeather() + ":" + w.getTemp_c()  
  72.         // + ":" + w.getIcon());  
  73.         remoteViews.setTextViewText(R.id.condition, MyWeather.weather1);  
  74.         remoteViews.setTextViewText(R.id.tem, (MyWeather.temp1));  
  75.         // 根据图片名,获取天气图片资源  
  76.         // remoteViews.setImageViewResource(  
  77.         // R.id.weather,  
  78.         // getApplicationContext().getResources().getIdentifier(  
  79.         // w.getIcon(), "drawable", "com.way.apptest"));  
  80.         if (MyWeather.img1 != null || !"".equals(MyWeather.img1))   
  81.             remoteViews.setImageViewResource(R.id.weather,  
  82.                     weatherImg[Integer.parseInt(MyWeather.img1)]);  
  83.         // 执行更新  
  84.         ComponentName componentName = new ComponentName(  
  85.                 getApplicationContext(), App.class);  
  86.         AppWidgetManager.getInstance(getApplicationContext()).updateAppWidget(  
  87.                 componentName, remoteViews);  
  88.     }  
  89.   
  90.     @Override  
  91.     public IBinder onBind(Intent intent) {  
  92.         return null;  
  93.     }  
  94.   
  95.     @Override  
  96.     public void onCreate() {  
  97.         super.onCreate();  
  98.         remoteViews = new RemoteViews(getApplication().getPackageName(),  
  99.                 R.layout.main);// 实例化RemoteViews  
  100.         if (isNetworkAvailable()) {  
  101.             MyWeather.getWeather();// json解析中国天气网天气  
  102.         } else {  
  103.             toast();  
  104.         }  
  105.         updateTime();// 第一次运行时先更新一下时间和天气  
  106.         updateWeather();  
  107.         // 点击天气图片,进入MainActivity  
  108.         Intent intent = new Intent(getApplicationContext(), MainActivity.class);  
  109.         PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),  
  110.                 0, intent, 0);  
  111.         remoteViews.setOnClickPendingIntent(R.id.weather, pi);  
  112.   
  113.         // 定义一个定时器去更新天气。实际开发中更新时间间隔可以由用户设置,  
  114.         new Timer().scheduleAtFixedRate(new TimerTask() {  
  115.             @Override  
  116.             public void run() {  
  117.                 Message msg = handler.obtainMessage();  
  118.                 msg.what = UPDATE;  
  119.                 handler.sendMessage(msg);  
  120.             }  
  121.         }, 13600 * 1000);// 每小时更新一次天气  
  122.     }  
  123.   
  124.     private void updateTime() {  
  125.         Date date = new Date();  
  126.         // 定义SimpleDateFormat对象  
  127.         SimpleDateFormat df = new SimpleDateFormat("HHmm");  
  128.         // 将当前时间格式化成HHmm的形式  
  129.         String timeStr = df.format(date);  
  130.   
  131.         for (int i = 0; i < timeStr.length(); i++) {  
  132.             // 将第i个数字字符转换为对应的数字  
  133.             int num2 = Integer.parseInt(timeStr.substring(i, i + 1));  
  134.             // 将第i个图片的设为对应的数字图片  
  135.             remoteViews.setImageViewResource(dateViews[i], imgs[num2]);  
  136.         }  
  137.         remoteViews.setTextViewText(R.id.city, MyWeather.city);  
  138.         remoteViews.setTextViewText(R.id.date, "0" + (date.getMonth() + 1)  
  139.                 + "-" + date.getDate() + " 周" + date.getDay());  
  140.         ComponentName componentName = new ComponentName(getApplication(),  
  141.                 App.class);  
  142.         AppWidgetManager.getInstance(getApplication()).updateAppWidget(  
  143.                 componentName, remoteViews);  
  144.     }  
  145.   
  146.     @Override  
  147.     public void onStart(Intent intent, int startId) {  
  148.         // 注册系统每分钟提醒广播(注意:这个广播只能在代码中注册)  
  149.         IntentFilter updateIntent = new IntentFilter();  
  150.         updateIntent.addAction("android.intent.action.TIME_TICK");  
  151.         registerReceiver(mTimePickerBroadcast, updateIntent);  
  152.         super.onStart(intent, startId);  
  153.     }  
  154.   
  155.     @Override  
  156.     public void onDestroy() {  
  157.         // 注销系统的这个广播  
  158.         unregisterReceiver(mTimePickerBroadcast);  
  159.         //被系统干掉后,服务重启,做一次流氓软件,哈哈  
  160.         Intent intent = new Intent(getApplicationContext(), UpdateService.class);  
  161.         getApplication().startService(intent);   
  162.         super.onDestroy();  
  163.     }  
  164.   
  165.     /** 
  166.      * 判断手机网络是否可用 
  167.      *  
  168.      * @param context 
  169.      * @return 
  170.      */  
  171.     private boolean isNetworkAvailable() {  
  172.         ConnectivityManager mgr = (ConnectivityManager) getApplicationContext()  
  173.                 .getSystemService(Context.CONNECTIVITY_SERVICE);  
  174.         NetworkInfo[] info = mgr.getAllNetworkInfo();  
  175.         if (info != null) {  
  176.             for (int i = 0; i < info.length; i++) {  
  177.                 if (info[i].getState() == NetworkInfo.State.CONNECTED) {  
  178.                     return true;  
  179.                 }  
  180.             }  
  181.         }  
  182.         return false;  
  183.     }  
  184.     <p>/** 
  185.   * 在非Activity中弹出对话框 
  186.   *  
  187.   * @param context 
  188.   */  
  189.  private void showDialog(final Context context) {  
  190.   // TODO Auto-generated method stub  
  191.   /* create ui */  
  192.   final AlertDialog dialog;  
  193.   AlertDialog.Builder builder = new AlertDialog.Builder(context);  
  194.   builder.setTitle("提示").setMessage("WiFi连接不可用");  
  195.   builder.setPositiveButton("前往打开"new OnClickListener() {</p><p>   public void onClick(DialogInterface dialog, int which) {  
  196.     Intent go2wifi = new Intent(  
  197.       android.provider.Settings.ACTION_WIRELESS_SETTINGS);  
  198.     go2wifi.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  199.     context.startActivity(go2wifi);</p><p>   }  
  200.   }).setNegativeButton("下次再说"null);  
  201.   dialog = builder.create();  
  202.   dialog.getWindow()  
  203.     .setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);  
  204.   // d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY);  
  205.   dialog.show();</p><p>  /* set size & pos */  
  206.   WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();  
  207.   WindowManager wm = (WindowManager) context  
  208.     .getSystemService(Context.WINDOW_SERVICE);  
  209.   Display display = wm.getDefaultDisplay();  
  210.   if (display.getHeight() > display.getWidth()) {  
  211.    // lp.height = (int) (display.getHeight() * 0.5);  
  212.    lp.width = (int) (display.getWidth() * 1.0);  
  213.   } else {  
  214.    // lp.height = (int) (display.getHeight() * 0.75);  
  215.    lp.width = (int) (display.getWidth() * 0.5);  
  216.   }  
  217.   dialog.getWindow().setAttributes(lp);  
  218.  }  
  219. </p>//记得加上权限     <!-- 系统对话框权限 -->  
  220.  //   <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>  
  221.     }  



三:这是桌面组件主要布局文件,如果大家有什么好的建议,欢迎提,本人对布局不是特别擅长。

 

[html]  view plain  copy
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/bg"  
  4.     android:layout_width="wrap_content"  
  5.     android:layout_height="wrap_content"  
  6.     android:layout_gravity="center"  
  7.     android:background="@drawable/ww_bg"  
  8.     android:orientation="vertical" >  
  9.   
  10.     <LinearLayout  
  11.         android:id="@+id/time"  
  12.         android:layout_width="wrap_content"  
  13.         android:layout_height="wrap_content"  
  14.         android:layout_gravity="center"  
  15.         android:orientation="horizontal" >  
  16.   
  17.         <LinearLayout  
  18.             android:id="@+id/linearLayout1"  
  19.             android:layout_width="wrap_content"  
  20.             android:layout_height="wrap_content"  
  21.             android:background="@drawable/wd_bg"  
  22.             android:orientation="horizontal" >  
  23.   
  24.             <ImageView  
  25.                 android:id="@+id/h1"  
  26.                 android:layout_width="wrap_content"  
  27.                 android:layout_height="fill_parent"  
  28.                 android:paddingLeft="10dip"  
  29.                 android:src="@drawable/n0" />  
  30.   
  31.             <ImageView  
  32.                 android:id="@+id/h2"  
  33.                 android:layout_width="wrap_content"  
  34.                 android:layout_height="fill_parent"  
  35.                 android:src="@drawable/n0" />  
  36.         </LinearLayout>  
  37.   
  38.         <TextView  
  39.             android:layout_width="30dip"  
  40.             android:layout_height="wrap_content" />  
  41.   
  42.         <LinearLayout  
  43.             android:id="@+id/linearLayout2"  
  44.             android:layout_width="wrap_content"  
  45.             android:layout_height="wrap_content"  
  46.             android:background="@drawable/wd_bg"  
  47.             android:orientation="horizontal" >  
  48.   
  49.             <ImageView  
  50.                 android:id="@+id/m1"  
  51.                 android:layout_width="wrap_content"  
  52.                 android:layout_height="match_parent"  
  53.                 android:paddingLeft="10dip"  
  54.                 android:src="@drawable/n0" />  
  55.   
  56.             <ImageView  
  57.                 android:id="@+id/m2"  
  58.                 android:layout_width="wrap_content"  
  59.                 android:layout_height="match_parent"  
  60.                 android:src="@drawable/n0" />  
  61.         </LinearLayout>  
  62.     </LinearLayout>  
  63.   
  64.     <LinearLayout  
  65.         android:id="@+id/linearLayout3"  
  66.         android:layout_width="wrap_content"  
  67.         android:layout_height="wrap_content"  
  68.         android:layout_gravity="center_horizontal"  
  69.         android:gravity="center"  
  70.         android:orientation="horizontal" >  
  71.   
  72.         <LinearLayout  
  73.             android:layout_width="100dip"  
  74.             android:layout_height="wrap_content"  
  75.             android:orientation="vertical"  
  76.             android:paddingLeft="10dip" >  
  77.   
  78.             <TextView  
  79.                 android:id="@+id/city"  
  80.                 android:layout_width="wrap_content"  
  81.                 android:layout_height="wrap_content"  
  82.                 android:textColor="#000000"  
  83.                 android:textSize="20sp" />  
  84.   
  85.             <TextView  
  86.                 android:id="@+id/condition"  
  87.                 android:layout_width="wrap_content"  
  88.                 android:layout_height="wrap_content"  
  89.                 android:text="@string/neterror"  
  90.                 android:textColor="#000000"  
  91.                 android:textSize="18sp" />  
  92.         </LinearLayout>  
  93.   
  94.         <ImageView  
  95.             android:id="@+id/weather"  
  96.             android:layout_width="wrap_content"  
  97.             android:layout_height="wrap_content"  
  98.             android:layout_gravity="center_horizontal|top"  
  99.             android:src="@drawable/sunny" />  
  100.   
  101.         <LinearLayout  
  102.             android:layout_width="100dip"  
  103.             android:layout_height="wrap_content"  
  104.             android:gravity="right"  
  105.             android:orientation="vertical"  
  106.             android:paddingRight="10dip" >  
  107.   
  108.             <TextView  
  109.                 android:id="@+id/date"  
  110.                 android:layout_width="wrap_content"  
  111.                 android:layout_height="wrap_content"  
  112.                 android:textColor="#000000"  
  113.                 android:textSize="18sp" />  
  114.   
  115.             <TextView  
  116.                 android:id="@+id/tem"  
  117.                 android:layout_width="wrap_content"  
  118.                 android:layout_height="wrap_content"  
  119.                 android:text="@string/neterror"  
  120.                 android:textColor="#000000"  
  121.                 android:textSize="18sp" />  
  122.         </LinearLayout>  
  123.     </LinearLayout>  
  124.   
  125. </LinearLayout>  

 

 

 

 


四:谷歌天气不给力,所以我就简单的给出获取天气的核心代码。这里简单的做了一下乱码处理。

 

[java]  view plain  copy
 
  1.  * @author way  
  2.  *   
  3.  */  
  4. public class GetWeather {  
  5.     /** 
  6.      *  
  7.      * @return 天气对象 
  8.      */  
  9.     public Weather googleWeather() {  
  10.         String str = "http://www.google.com/ig/api?hl=zh_cn&weather=shenzhen";  
  11.         try {  
  12.             URL url = new URL(str);  
  13.             InputStream in = url.openStream();  
  14.             ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  15.             int len = -1;  
  16.             while ((len = in.read()) != -1) {  
  17.                 bos.write(len);  
  18.             }  
  19.             InputStream is = new ByteArrayInputStream(bos.toString("GBK")  
  20.                     .getBytes("utf-8"));  
  21.             // 从流中获取文档到本地内存  
  22.             Document doc = DocumentBuilderFactory.newInstance()  
  23.                     .newDocumentBuilder().parse(is);  
  24.             // 从文档中得到名字为current_conditions的第一个节点下的所有子节点(一个集合)  
  25.             NodeList nodeList = doc.getElementsByTagName("current_conditions")  
  26.                     .item(0).getChildNodes();  
  27.             // 得到nodeList下第一个节点的第一个元素内容(即当前天气)  
  28.             String condition = nodeList.item(0).getAttributes().item(0)  
  29.                     .getNodeValue();  
  30.             // 当前温度  
  31.             String temp_c = nodeList.item(2).getAttributes().item(0)  
  32.                     .getNodeValue();  
  33.             // 当前湿度  
  34. //          String humidity = nodeList.item(3).getAttributes().item(0)  
  35. //                  .getNodeValue();  
  36.             // 当前图片路径  
  37.             String iconPath = nodeList.item(4).getAttributes().item(0)  
  38.                     .getNodeValue();  
  39.             // 当前风向  
  40. //          String wind_condition = nodeList.item(5).getAttributes().item(0)  
  41. //                  .getNodeValue();  
  42.             Weather w = new Weather();  
  43.             w.setWeather(condition);  
  44.             w.setTemp_c(temp_c);  
  45.             // 从图片路径中获取图片的名字  
  46.             String icon = iconPath.substring(iconPath.lastIndexOf("/") + 1,  
  47.                     iconPath.indexOf("."));  
  48.             w.setIcon(icon);  
  49.             return w;  
  50.         } catch (MalformedURLException e) {  
  51.             e.printStackTrace();  
  52.         } catch (IOException e) {  
  53.             e.printStackTrace();  
  54.         } catch (SAXException e) {  
  55.             e.printStackTrace();  
  56.         } catch (ParserConfigurationException e) {  
  57.             e.printStackTrace();  
  58.         }  
  59.         return null;  
  60.     }  
  61. }  

今天再修改解析中国天气网的json代码:

[html]  view plain  copy
 
  1. public class MyWeather {  
  2.     public static String city;  
  3.     public static String temp1;  
  4.     public static String weather1;  
  5.     public static String img1;  
  6.   
  7.     public static void getWeather() {  
  8.         try {  
  9.             URL url = new URL("http://m.weather.com.cn/data/101250101.html");  
  10.             InputStream is = url.openStream();  
  11.             ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  12.             int len = -1;  
  13.             byte[] buffer = new byte[1024];  
  14.             while ((len = is.read(buffer)) != -1) {  
  15.                 bos.write(buffer, 0, len);  
  16.             }  
  17.             String info = bos.toString("utf-8");  
  18.             JSONObject dataJson = new JSONObject(info);  
  19.             JSONObject json = dataJson.getJSONObject("weatherinfo");  
  20.             city = json.getString("city");  
  21.             temp1 = json.getString("temp1");  
  22.             weather1 = json.getString("weather1");  
  23.             img1 = json.getString("img1");  
  24.             System.out.println(city);is.close();bos.close();  
  25.         } catch (MalformedURLException e) {  
  26.             e.printStackTrace();  
  27.         } catch (IOException e) {  
  28.             e.printStackTrace();  
  29.         } catch (JSONException e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.     }  
  33.   
  34. }  

 

好了,大功告成,这是发表第三遍了,前两次都是悲剧,哎,不知道是网络问题还是我电脑问题,提交没有反应。总之很蛋疼!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐