详解从零开始创建Android主屏幕Widget

移动开发
创建Android主屏幕Widget是本文要介绍的内容,主要是来了解Android Widget 的创建过程,文中很详细的讲解了Android Widget创建过程,来看内容。

创建Android主屏幕Widget是本文要介绍的内容,主要是来了解Android Widget 的创建过程,文中很详细的讲解了Android Widget创建过程,一起来看本文详解。

当最基本的控件,如Clock和PictureFrameHomeScreen,随第一款Android手机的发布后,Android用户就开始尝试编写各种应用Widget(小工具)了,随着WidgetAPI的公开,为开发人员带来了全新有趣的开发模式,除了传统的电话应用外,还可以做其它方面的应用开发。

开发人员可以使用WidgetAPI(包含在Android1.5中,最新版本已经到Android2.0了)创建简单的控件,然后在新的Widget中显示和使用这些控件。

本文向你介绍如何从零开始创建一个主屏幕应用Widget,通过使用AlarmManager接口,以用户设定的时间间隔更新图片。你将看到如何创建一个Widget,以及如何随机地从一组图片中选择一张图片显示,根据用户设定的时间间隔周期性改变显示的图片。

创建一个简单的Widget包括以下几个步骤:

1、创建一个RemoteView,由它为Widget提供用户界面;

2、将RemoteView绑定一个Activity(行为)实现AppWidgetProvider接口;

3、在Androidmanifest配置文件中提供Widget的关键配置信息。

项目准备

一个Widget就是一个处理特定行为的BroadcastReceiver,AppWidgetProvider接口为开发人员提供了一个框架来简化处理这些行为,它包括以下方法:

1、onEnabled():创建第一个Widget时调用,如果可以,应在这里进行全局初始化。

2、onDisabled():它和onEnabled()相反,创建最后一个Widget时才调用它,如果可以,应在这里进行全局清理。

3、onUpdate():当Widget需要更新它的View时调用,用户第一次创建Widget时也需要调用它。

4、onDeleted():当Widget的一个特定实例被删除时调用,清理特定实例应放在这里进行。

5、onReceive():此方法默认情况下处理BroadcastReceiver行为,并调用上面的方法(警告:根据相关文档记载,需要开发人员自己处理某些特殊情况,更多信息请看下面的说明)。

作者注:点击此链接(http://groups.google.com/group/android-developers/browse_thread/thread/365d1ed3aac30916/e405ca19df2170e2?pli=1)检查有关AppWidget框架的缺陷信息,讨论内容包括解决此问题的代码(也可从本文下载示例代码,http://www.developer.com/img/2009/08/3833306_appwidget_code.zip),如果它们并不存在,可以将Widget标识符传递给onUpdate()方法。

为了让Android识别Widget,需要在manifest文件中加入一个标准的标记,下面的代码片段显示了一个示例:

  1.  receiver android:name=“ImagesWidgetProvider”  
  2.  intent-filter  
  3.  action  
  4. android:name=“android.appwidget.action.APPWIDGET_UPDATE” /  
  5.  /intent-filter  
  6.  meta-data  
  7.  android:name=“android.appwidget.provider”  
  8.  android:resource=“@xml/imageswidget_info” /  
  9. /receiver 

你可能已经注意到,和常见的定义不一样,小节引用了一个XML文件资源,这个文件为Widget定义了额外的数据,与AppWidgetProviderInfo类一致,这里定义的信息是不变的,因此这个例子不包括updatePeriodMillis的值,因为这个程序允许用户修改与更新时间,如果你在这里分配updatePeriodMillis,它就不能这样做。下面是imageswidget_info.xml文件的完整代码:

  1. ?xml version=“1.0” encoding=“utf-8”?  
  2.  appwidget-provider  
  3. xmlns:android=“http://schemas.android.com/apk/res/android”  
  4.  android:minWidth=“146dp”  
  5.  android:minHeight=“146dp”  
  6.  android:initialLayout=“@layout/widget”  
  7.  android:configure=  
  8.  “com.mamlambo.imageswidget.ImagesWidgetConfiguration” / 

标记定义了Widget的大小,默认布局和创建Widget实例时的启动行为配置,为了让Widget在主屏幕上更好地显示,Widget必须保持一定的大小,主屏幕分为特定大小的单元格,Google提供的基本原则是用你想占用的单元格数量乘以74,再减去2。在这个例子中,Widget应该是一个正方形,长和宽都各占两个单元格,因此大小就是74*2-2=146.

实现onUpdate()

不显示内容的Widget是没有用的,幸好这个Widget的RemoteView对象很容易实现,它使用一组存储在应用程序的drawable资源目录下的图片,程序使用R.drawable.[imagename]引用这些图片资源,代码需要创建一个数组容纳图片的名字,这样从这些图片名字中随机选择一个就可以了。下面的代码片段显示了onUpdate()的实现,它随机显示一张图片:

  1. @Override  
  2.  public void onUpdate(Context context,  
  3.  AppWidgetManager appWidgetManager,  
  4.  int[] appWidgetIds) {  
  5.  for (int appWidgetId : appWidgetIds) {  
  6.  int imageNum = (new  
  7.  java.util.Random().nextInt(IMAGES.length));  
  8.  RemoteViews remoteView = new 
  9.  RemoteViews(context.getPackageName(),  
  10.  R.layout.widget);  
  11.  remoteView.setImageViewResource(  
  12.  R.id.image, IMAGES[imageNum]);  
  13.  appWidgetManager.updateAppWidget(  
  14.  appWidgetId, remoteView);  
  15. }  
  16.  } 

注意onUpdate()方法使用Widget实例列表作为最后的参数,每个实例必须分开处理,由于AppWidget框架的现有缺陷,有些实例可能不可见或不能启用,但在这个例子中可以忽略这些问题。请记住,你自己在实现时可能想跟踪哪个Widget是真正激活的。

你可以下载本文提供的代码(http://www.developer.com/img/2009/08/3833306_appwidget_code.zip),查看其中的R.layout.widgetXML布局定义,它基本上只是一个ImageView,RemoteViews只能使用一组有限的View对象,包括Button,ImageButton,ImageView,TextView,AnalogClock,Chronometer和ProgressBar,并且只能在FrameLayout,LinearLayout或RelativeLayout内使用。请保持RemoteView简单,因为访问是通过setImageViewResource()和setTextViewText()方法控制的,它们的目的是在另一个进程内画一个View,因此你的应用程序比正常布局要少些控制。

自此,Widget最基本的部分完成了,但为了让用户配置图片更新之间的时间间隔,你必须要实现配置

Activity,然后处理RemoteView更新的调度问题。

实现Widget的Activity配置

和manifest文件中定义的ImagesWidgetConfiguration类似,任何Activity配置都有两个特殊情况:

1、只要一启动,它的目的就是返回结果,因此必须要调用setResult()方法返回一个适当的结果(结果要么是RESULT_CANCELED或RESULT_OK)。

2、在设置结果时,Widget标识值必须放在额外引用的AppWidgeManager.EXTRA_APPWIDGET_ID中。

下面的代码片段显示了如何处理这两个例外,如果用户中途退出Activity,调用setResult()将默认值设为RESULT_CANCELED。

  1. Bundle extras = launchIntent.getExtras();  
  2.  if (extras != null) {  
  3.  appWidgetId = extras.getInt(  
  4.  AppWidgetManager.EXTRA_APPWIDGET_ID,  
  5.  AppWidgetManager.INVALID_APPWIDGET_ID);  
  6.  Intent cancelResultValue = new Intent();  
  7.  cancelResultValue.putExtra(  
  8.  AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);  
  9.  setResult(RESULT_CANCELED, cancelResultValue);  
  10.  } 

除了这两个限制外,你可能还要实现你喜欢的Activity配置,图1显示了这个例子的简单Activity配置,如果你RESULT_CANCELED,Widget不会显示给用户,如果你返回RESULT_OK,用户才看得见Widget,你可以使用任意存储机制保存Widget实例的配置数据,这个例子使用的是SharedPreferences接口,存储Widget标识更新之间的时间,你可以下载本文配套的代码查看完整的实现(http://www.developer.com/img/2009/08/3833306_appwidget_code.zip)。

\

图1配置界面:这个简单的配置界面让用户设置图片刷新间隔时间

实现更新调度

正如前面提到的,AppWidgetProviderInfo类中的配置值是不变的,因为updateTimeMillis值就在这个类中,任何有AppWidgetProviderInfo的Widget实例,只要设置了这个值,Widget就会根据其频率更新,没有办法修改它,因为这个应用程序应该让用户配置Widget中图片刷新的频率,因此你必须自动动手实现。

AlarmManager类是最常用的更新机制,因为它支持重复通知,这些通知是将被触发的简单的PendingIntent对象。

你可能会想你可以创建一个具有AppWidgetManager.ACTION_APPWIDGET_UPDATE的Intent对象,然后给特定的Widget标识符设置额外的值,接着你就可以通过调用AlarmManager的setRepeating()方法,采用调度机制反复地更新,遗憾的是,这种做法行不通,Android系统反复使用Intents匹配行为和方案值,那些“额外的”值是不会拿去对比的。实际上,解决方案非常简单:首先为你的Widget定义一个方案,然后用它定义唯一的Intent实例。下面的代码片段显示如何实现这个目标:

  1.  Intent widgetUpdate = new Intent();  
  2.  widgetUpdate.setAction(  
  3.  AppWidgetManager.ACTION_APPWIDGET_UPDATE);  
  4.  widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS,  
  5.  new int[] { appWidgetId });  
  6.  // make this pending intent unique  
  7.  widgetUpdate.setData(  
  8.  Uri.withAppendedPath(Uri.parse(  
  9.  ImagesWidgetProvider.URI_SCHEME + “://widget/id/”),  
  10.  String.valueOf(appWidgetId)));  
  11.  PendingIntent newPending = PendingIntent.getBroadcast(  
  12.  getApplicationContext(), 0, widgetUpdate,  
  13.  PendingIntent.FLAG_UPDATE_CURRENT);  
  14.  // now schedule it  
  15. AlarmManager alarms = (AlarmManager) getApplicationContext()。  
  16.  getSystemService(Context.ALARM_SERVICE);  
  17.  alarms.setRepeating(AlarmManager.ELAPSED_REALTIME,  
  18.  SystemClock.elapsedRealtime(), updateRateSeconds * 1000,  
  19.  newPending); 

在ImagesWidgetConfigurationActivity中你会发现前面的代码,manifest文件显示块处理这个特定的scheme,你也需要在AppWidgetProvider接口的onDeleted()方法内停止重复的警报。最后,当手机重启后,更新调度也必须重启。

在AppWidgetProvider的onReceive()方法内提供了一个简单的解决方案,首先,检查更新行为,如果它是一个更新,确保它不包含scheme值,如果它不是更新,那么调度AlarmManager的PendingIntent不会触发这个行为,在这种情况下,检查每个Widget标识符是否有一个配置选项值,如果没有,那么在配置Widget之前,你知道这个更新行为被接收到。但如果选项值有效,那么你知道可以调度PendingIntent,在onReceive()方法中你可以看到完整的逻辑实现。

这个方案允许多个Widget同时显示,如图2所示。更新也可以以不同的频率进行,每个显示的图片是从图片集中随机选择的。

\

图2多个Widget实例:这里显示了两个不同的同时运行的实例

至此,你已经看到了如何在Android平台上创建一个基本的Widget,每个Widget实例按独立的方式调度,由用户自行配置,但AppWidge框架不直接支持,因此需要自动动手编码实现。

小结:详解从零开始创建Android主屏幕Widget的内容介绍完了,希望通过Android Widget 创建内容的学习能对你有所帮助!

责任编辑:zhaolei 来源: 互联网
相关推荐

2018-01-16 11:00:25

2018-03-14 11:15:06

2011-09-07 13:18:40

Android Wid

2018-03-05 09:31:23

2014-07-22 13:09:21

android

2019-05-14 10:43:17

图标UI设计界面

2015-11-17 16:11:07

Code Review

2019-01-18 12:39:45

云计算PaaS公有云

2018-04-18 07:01:59

Docker容器虚拟机

2020-07-02 15:32:23

Kubernetes容器架构

2010-05-26 17:35:08

配置Xcode SVN

2018-09-14 17:16:22

云计算软件计算机网络

2011-09-09 20:14:58

Android Wid

2023-10-24 16:44:24

RubyDNS

2010-07-13 09:02:19

Widget开发

2018-01-09 10:05:55

神经网络ClojureCortex

2015-10-15 14:16:24

2011-04-06 15:55:50

开发webOS程序webOS

2015-03-06 17:24:56

Android应用安全安卓应用安全

2018-03-15 13:48:45

架构技术栈微信半月刊
点赞
收藏

51CTO技术栈公众号