(4.0.24.2)Android之桌面组件App Widget案例之高仿墨迹天气桌面组件
相信墨迹天气,大家都见过,他在时间显示和天气界面上,很吸引人,今天我就来模仿一下墨迹天气的桌面组件,但是由于谷歌在天朝频频被墙的缘故,所以我在今天测试的时候,解析xml文件的网页打不开,所以天气显示出了点问题,希望大家能理解,谢谢。(今天9月24日修改为解析中国天气网获取天气了,而且修改组件在桌面居中显示)。老规矩,先分享源代码:http://download.csdn.n
相信墨迹天气,大家都见过,他在时间显示和天气界面上,很吸引人,今天我就来模仿一下墨迹天气的桌面组件,但是由于谷歌在天朝频频被墙的缘故,所以我在今天测试的时候,解析xml文件的网页打不开,所以天气显示出了点问题,希望大家能理解,谢谢。(今天9月24日修改为解析中国天气网获取天气了,而且修改组件在桌面居中显示)。
老规矩,先分享源代码:http://download.csdn.net/detail/weidi1989/4597809
2013年07月06日更新版本:http://download.csdn.net/detail/weidi1989/5712051
好了,废话不多说,先上效果图:
再来看一下整个小项目的主体结构:
首先先声明一个桌面布局的xml文件,即app.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <!--
- 指定该桌面组件的基本配置信息:
- initialLayout:初始时显示的布局
- minWidth:桌面小控件的最小高度。
- minWidth:桌面小控件的最小宽度。
- updatePeriodMillis:更新频率
- -->
- <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
- android:initialLayout="@layout/main"
- android:minHeight="150dp"
- android:minWidth="200dp"
- android:updatePeriodMillis="0" >
- </appwidget-provider>
然后要在manifest文件中声明:
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.way.apptest"
- android:versionCode="1"
- android:versionName="1.0" >
- <uses-sdk
- android:minSdkVersion="8"
- android:targetSdkVersion="15" />
- <uses-permission android:name="android.permission.INTERNET" />
- <application
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <activity android:name="com.way.apptest.MainActivity" >
- </activity>
- <receiver android:name="com.way.apptest.App" >
- <!-- 指定桌面小控件的meta-data -->
- <meta-data
- android:name="android.appwidget.provider"
- android:resource="@xml/app" />
- <!-- 将该BroadcastReceiver当成桌面小控件 -->
- <intent-filter>
- <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
- </intent-filter>
- </receiver>
- <service android:name=".UpdateService" >
- </service>
- </application>
- </manifest>
主要代码:
一:定义一个App类继承AppWidgetProvider,然后再onUpdate方法中启动一个服务去更新时间和天气。
- /**
- * @author way
- */
- public class App extends AppWidgetProvider {
- private Intent intent;
- @Override
- public void onUpdate(Context context, AppWidgetManager appWidgetManager,
- int[] appWidgetIds) {
- intent = new Intent(context, UpdateService.class);
- context.startService(intent);
- super.onUpdate(context, appWidgetManager, appWidgetIds);
- }
- @Override
- public void onDeleted(Context context, int[] appWidgetIds) {
- context.stopService(intent);
- super.onDeleted(context, appWidgetIds);
- }
- }
二:本项目中最重要的部分,在这个服务中,我们注册一个广播接收者去接受系统每分钟时间时间变化的广播,从而来更新桌面时间,这样更省电哦,需要注意的是:这个广播接收者必须在代码中注册,在Manifest文件中注册是没有效果的。更新天气,我是通过定义一个定时器,由用户设置时间间隔来更新天气信息。
- package com.way.apptest;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Timer;
- import java.util.TimerTask;
- import android.app.AlertDialog;
- import android.app.PendingIntent;
- import android.app.Service;
- import android.appwidget.AppWidgetManager;
- import android.content.BroadcastReceiver;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.net.ConnectivityManager;
- import android.net.NetworkInfo;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Message;
- import android.widget.RemoteViews;
- import com.way.getWeather.MyWeather;
- /**
- * @author way
- */
- public class UpdateService extends Service {
- private static final int UPDATE = 0x123;
- private RemoteViews remoteViews;
- // 数字时间图片资源数组
- private int[] imgs = { R.drawable.n0, R.drawable.n1, R.drawable.n2,
- R.drawable.n3, R.drawable.n4, R.drawable.n5, R.drawable.n6,
- R.drawable.n7, R.drawable.n8, R.drawable.n9, };
- // 将显示小时、分钟的ImageView定义成数组
- private int[] dateViews = { R.id.h1, R.id.h2, R.id.m1, R.id.m2 };
- // 按照中国天气网的天气图片顺序排列好本地资源图片,我这里是随意的~嘿嘿
- private int[] weatherImg = { R.drawable.sunny, R.drawable.cloudy,
- R.drawable.chance_of_rain, R.drawable.chance_of_sleet,
- R.drawable.chance_of_snow, R.drawable.chance_of_storm,
- R.drawable.clock1, R.drawable.fog, R.drawable.haze,
- R.drawable.mist, R.drawable.mostly_sunny, R.drawable.mostly_cloudy,
- R.drawable.lower, R.drawable.middle };
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case UPDATE:
- // 更新天气
- updateTime();
- updateWeather();
- break;
- }
- }
- };
- // 广播接收者去接收系统每分钟的提示广播,来更新时间
- private BroadcastReceiver mTimePickerBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- updateTime();
- }
- };
- private void updateWeather() {
- // Weather w = new GetWeather().googleWeather();
- // if (w != null) {
- // System.out.println("当前天气:" + w.getWeather() + ":" + w.getTemp_c()
- // + ":" + w.getIcon());
- remoteViews.setTextViewText(R.id.condition, MyWeather.weather1);
- remoteViews.setTextViewText(R.id.tem, (MyWeather.temp1));
- // 根据图片名,获取天气图片资源
- // remoteViews.setImageViewResource(
- // R.id.weather,
- // getApplicationContext().getResources().getIdentifier(
- // w.getIcon(), "drawable", "com.way.apptest"));
- if (MyWeather.img1 != null || !"".equals(MyWeather.img1))
- remoteViews.setImageViewResource(R.id.weather,
- weatherImg[Integer.parseInt(MyWeather.img1)]);
- // 执行更新
- ComponentName componentName = new ComponentName(
- getApplicationContext(), App.class);
- AppWidgetManager.getInstance(getApplicationContext()).updateAppWidget(
- componentName, remoteViews);
- }
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- remoteViews = new RemoteViews(getApplication().getPackageName(),
- R.layout.main);// 实例化RemoteViews
- if (isNetworkAvailable()) {
- MyWeather.getWeather();// json解析中国天气网天气
- } else {
- toast();
- }
- updateTime();// 第一次运行时先更新一下时间和天气
- updateWeather();
- // 点击天气图片,进入MainActivity
- Intent intent = new Intent(getApplicationContext(), MainActivity.class);
- PendingIntent pi = PendingIntent.getActivity(getApplicationContext(),
- 0, intent, 0);
- remoteViews.setOnClickPendingIntent(R.id.weather, pi);
- // 定义一个定时器去更新天气。实际开发中更新时间间隔可以由用户设置,
- new Timer().scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- Message msg = handler.obtainMessage();
- msg.what = UPDATE;
- handler.sendMessage(msg);
- }
- }, 1, 3600 * 1000);// 每小时更新一次天气
- }
- private void updateTime() {
- Date date = new Date();
- // 定义SimpleDateFormat对象
- SimpleDateFormat df = new SimpleDateFormat("HHmm");
- // 将当前时间格式化成HHmm的形式
- String timeStr = df.format(date);
- for (int i = 0; i < timeStr.length(); i++) {
- // 将第i个数字字符转换为对应的数字
- int num2 = Integer.parseInt(timeStr.substring(i, i + 1));
- // 将第i个图片的设为对应的数字图片
- remoteViews.setImageViewResource(dateViews[i], imgs[num2]);
- }
- remoteViews.setTextViewText(R.id.city, MyWeather.city);
- remoteViews.setTextViewText(R.id.date, "0" + (date.getMonth() + 1)
- + "-" + date.getDate() + " 周" + date.getDay());
- ComponentName componentName = new ComponentName(getApplication(),
- App.class);
- AppWidgetManager.getInstance(getApplication()).updateAppWidget(
- componentName, remoteViews);
- }
- @Override
- public void onStart(Intent intent, int startId) {
- // 注册系统每分钟提醒广播(注意:这个广播只能在代码中注册)
- IntentFilter updateIntent = new IntentFilter();
- updateIntent.addAction("android.intent.action.TIME_TICK");
- registerReceiver(mTimePickerBroadcast, updateIntent);
- super.onStart(intent, startId);
- }
- @Override
- public void onDestroy() {
- // 注销系统的这个广播
- unregisterReceiver(mTimePickerBroadcast);
- //被系统干掉后,服务重启,做一次流氓软件,哈哈
- Intent intent = new Intent(getApplicationContext(), UpdateService.class);
- getApplication().startService(intent);
- super.onDestroy();
- }
- /**
- * 判断手机网络是否可用
- *
- * @param context
- * @return
- */
- private boolean isNetworkAvailable() {
- ConnectivityManager mgr = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo[] info = mgr.getAllNetworkInfo();
- if (info != null) {
- for (int i = 0; i < info.length; i++) {
- if (info[i].getState() == NetworkInfo.State.CONNECTED) {
- return true;
- }
- }
- }
- return false;
- }
- <p>/**
- * 在非Activity中弹出对话框
- *
- * @param context
- */
- private void showDialog(final Context context) {
- // TODO Auto-generated method stub
- /* create ui */
- final AlertDialog dialog;
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle("提示").setMessage("WiFi连接不可用");
- builder.setPositiveButton("前往打开", new OnClickListener() {</p><p> public void onClick(DialogInterface dialog, int which) {
- Intent go2wifi = new Intent(
- android.provider.Settings.ACTION_WIRELESS_SETTINGS);
- go2wifi.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(go2wifi);</p><p> }
- }).setNegativeButton("下次再说", null);
- dialog = builder.create();
- dialog.getWindow()
- .setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
- // d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY);
- dialog.show();</p><p> /* set size & pos */
- WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
- WindowManager wm = (WindowManager) context
- .getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- if (display.getHeight() > display.getWidth()) {
- // lp.height = (int) (display.getHeight() * 0.5);
- lp.width = (int) (display.getWidth() * 1.0);
- } else {
- // lp.height = (int) (display.getHeight() * 0.75);
- lp.width = (int) (display.getWidth() * 0.5);
- }
- dialog.getWindow().setAttributes(lp);
- }
- </p>//记得加上权限 <!-- 系统对话框权限 -->
- // <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- }
三:这是桌面组件主要布局文件,如果大家有什么好的建议,欢迎提,本人对布局不是特别擅长。
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/bg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:background="@drawable/ww_bg"
- android:orientation="vertical" >
- <LinearLayout
- android:id="@+id/time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="horizontal" >
- <LinearLayout
- android:id="@+id/linearLayout1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/wd_bg"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/h1"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:paddingLeft="10dip"
- android:src="@drawable/n0" />
- <ImageView
- android:id="@+id/h2"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:src="@drawable/n0" />
- </LinearLayout>
- <TextView
- android:layout_width="30dip"
- android:layout_height="wrap_content" />
- <LinearLayout
- android:id="@+id/linearLayout2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/wd_bg"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/m1"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingLeft="10dip"
- android:src="@drawable/n0" />
- <ImageView
- android:id="@+id/m2"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:src="@drawable/n0" />
- </LinearLayout>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/linearLayout3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:gravity="center"
- android:orientation="horizontal" >
- <LinearLayout
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="10dip" >
- <TextView
- android:id="@+id/city"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="#000000"
- android:textSize="20sp" />
- <TextView
- android:id="@+id/condition"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/neterror"
- android:textColor="#000000"
- android:textSize="18sp" />
- </LinearLayout>
- <ImageView
- android:id="@+id/weather"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|top"
- android:src="@drawable/sunny" />
- <LinearLayout
- android:layout_width="100dip"
- android:layout_height="wrap_content"
- android:gravity="right"
- android:orientation="vertical"
- android:paddingRight="10dip" >
- <TextView
- android:id="@+id/date"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="#000000"
- android:textSize="18sp" />
- <TextView
- android:id="@+id/tem"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/neterror"
- android:textColor="#000000"
- android:textSize="18sp" />
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
四:谷歌天气不给力,所以我就简单的给出获取天气的核心代码。这里简单的做了一下乱码处理。
- * @author way
- *
- */
- public class GetWeather {
- /**
- *
- * @return 天气对象
- */
- public Weather googleWeather() {
- String str = "http://www.google.com/ig/api?hl=zh_cn&weather=shenzhen";
- try {
- URL url = new URL(str);
- InputStream in = url.openStream();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- int len = -1;
- while ((len = in.read()) != -1) {
- bos.write(len);
- }
- InputStream is = new ByteArrayInputStream(bos.toString("GBK")
- .getBytes("utf-8"));
- // 从流中获取文档到本地内存
- Document doc = DocumentBuilderFactory.newInstance()
- .newDocumentBuilder().parse(is);
- // 从文档中得到名字为current_conditions的第一个节点下的所有子节点(一个集合)
- NodeList nodeList = doc.getElementsByTagName("current_conditions")
- .item(0).getChildNodes();
- // 得到nodeList下第一个节点的第一个元素内容(即当前天气)
- String condition = nodeList.item(0).getAttributes().item(0)
- .getNodeValue();
- // 当前温度
- String temp_c = nodeList.item(2).getAttributes().item(0)
- .getNodeValue();
- // 当前湿度
- // String humidity = nodeList.item(3).getAttributes().item(0)
- // .getNodeValue();
- // 当前图片路径
- String iconPath = nodeList.item(4).getAttributes().item(0)
- .getNodeValue();
- // 当前风向
- // String wind_condition = nodeList.item(5).getAttributes().item(0)
- // .getNodeValue();
- Weather w = new Weather();
- w.setWeather(condition);
- w.setTemp_c(temp_c);
- // 从图片路径中获取图片的名字
- String icon = iconPath.substring(iconPath.lastIndexOf("/") + 1,
- iconPath.indexOf("."));
- w.setIcon(icon);
- return w;
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- }
- return null;
- }
- }
今天再修改解析中国天气网的json代码:
- public class MyWeather {
- public static String city;
- public static String temp1;
- public static String weather1;
- public static String img1;
- public static void getWeather() {
- try {
- URL url = new URL("http://m.weather.com.cn/data/101250101.html");
- InputStream is = url.openStream();
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- int len = -1;
- byte[] buffer = new byte[1024];
- while ((len = is.read(buffer)) != -1) {
- bos.write(buffer, 0, len);
- }
- String info = bos.toString("utf-8");
- JSONObject dataJson = new JSONObject(info);
- JSONObject json = dataJson.getJSONObject("weatherinfo");
- city = json.getString("city");
- temp1 = json.getString("temp1");
- weather1 = json.getString("weather1");
- img1 = json.getString("img1");
- System.out.println(city);is.close();bos.close();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }
好了,大功告成,这是发表第三遍了,前两次都是悲剧,哎,不知道是网络问题还是我电脑问题,提交没有反应。总之很蛋疼!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)