应用小部件是可以嵌入其它应用(例如主屏幕)并收到定期更新的微型应用视图。这些视图在用户界面中被叫做小部件,并可以用应用小部件提供者发布。可以容纳其他应用部件的应用组件叫做应用部件的宿主(1)。下面的截图显示了音乐应用部件。

本文档描述了如何使用应用部件提供者发布应用小部件。关于创建自己的应用部件宿主来容纳应用部件,请参考应用部件宿主。

小部件设计

如何设计自己的小部件的信息,请阅读小部件设计向导。

基础知识

要创建应用小部件,你需要如下:

此外,你可以实现一个应用部件配置活动。这是一个可选的活动。它在用户添加你的应用部件时启动,并且让他或她可以在创建时修改应用部件设置。

下面的小节描述了如何建立这些组件中的每一个。

在清单中声明应用部件

首先,在应用的AndroidManifest.xml的中声明AppWidgetProvider类。例如:

元素需要android:name属性,它指定了应用组件使用的AppWidgetProvider。

元素必须包含在元素中并带有android:name属性。这个属性指明AppWidgetProvider接受ACTION_APPWIDGET_UPDATE广播。这是你唯一必须明确声明的广播。AppWidgetManager会在适当的时候向所有的AppWidgetProvider发送应用部件广播。

元素指定AppWidgetProviderInfo资源并需要如下属性:

添加AppWidgetProviderInfo元数据

AppWidgetProviderInfo定义了应用部件的必要特性,例如最小布局尺寸,它的初始布局资源,多长时间更新应用部件,和(可选的)创建时启动的配置活动。使用一个单独的元素在XML资源中定义AppWidgetProviderInfo对象并奖它保存在工程的res/xml/文件夹。

例如:

这里是属性的一个总结:

查看应用部件设计引导了解更多关于调整应用部件大小的信息。

注意:为了让你的应用部件可在设备间移植,它的最小尺寸应该永远大于4x4个单元格。

查阅应用部件设计向导了解更多关于调正应用部件大小的信息。

注意:如果设备在是时候更新(由updatePeriodMillis定义)的时候休眠,那时设备将唤醒以执行更新。如果不超过一小时一次更新,这才不会引起影响电池寿命的显著问题。但是,如果你需要更频繁地更新,且/或不需要在设备休眠的时候更新,那么你换而可以基于不会唤醒设备的警报来执行更新。使用AlarmManager建立一个带有你的应用部件提供者接收的意图的警报做到这点。将警报类型设为ELAPSED_REALTIME或RTC,它将仅会在设备唤醒时发送警报。然后设置updatePeriodMillis为0(“0”)。

参考AppWidgetProviderInfo类中元素接收的属性了解更多信息。

创建应用部件布局

你必须在XML中为你的应用定义初始布局并将它保存在项目的res/layout/目录。你可以用下面列出的视图对象设计你的应用部件,但是,在你开始设计你的应用部件之前,请阅读理解应用部件向导。

如果你熟悉布局,那么创建应用部件布局是简单的。但是,你必须知道应用部件布局是基于RemoteViews的,它不支持各种布局和视图部件。

RemoteView 对象 (并且,所以,也是一个应用部件) 可以支持如下布局类:

和如下部件类:

不支持这些类的后裔。

RemoteView也支持ViewStub,它是一个不可见的,大小为0的视图,你可以在运行时延迟为它填充布局资源。

给应用部件添加边距

部件通常不应该延伸到屏幕边缘并且不应该在视觉上与其他部件齐平,因此你应该在你的部件框架(2)四周所有边上增加边距。

由于在安卓系统4.0版本中,应用部件会被自动地在部件框架和应用部件边框之间给予填充,以提供与其他部件和用户主屏幕上的图标的更好的对齐。要利用这个强烈推荐行为的优势,将应用的targetSdkVersion设为14或更高。

写一个单独的拥有应用于早期平台版本的自定义边距,且在安卓4.0或更高版本没有多余边距的布局是容易的:

  1. 设置应用的targetSdkVersion为14或更高。

  2. 创建一个如下的布局,它为自己的边距引用了尺寸资源:

  3. 创建两个尺寸资源,一个是在res/values/中用于提供安卓4.0之前版本的自定义边距资源,一个是在/res/values-v14/中用于提供给安卓4.0版本部件的无多余边距资源:

  4. res/values/dimens.xml:

  5. res/values-v14/dimens.xml:

另一种选择是简单地在默认情况下建立额外的边距到9宫格拉伸图片(译者注:横竖分成9份,中间5格是可重复像素的图片,一般用于常用的窗体背景)背景资源中,并为API级别14(译者注:就是安卓4.0版本,这是它的相应的API版本),或之后的版本提供没有边距的9宫格图片。

使用AppWidgetProvider类

你必须在AndroidManifest(参考上面的在清单文件中声明应用部件)中使用元素将AppWidgetProvider类实现声明为广播接收者。

AppWidgetProvider扩展了BroadcastReceiver类作为一个方便处理应用部件广播的类。AppWidgetProvider仅接收与应用部件相关的事件广播,例如当应用部件被更新,删除,启用,禁用的时候。当这些广播事件发生时,AppWidgetProvider接受如下方法调用:

onUpdate这个方法被调用以在每经过一段间隔更新应用部件,这段间隔由AppWidgetProviderInfo(参考上面的添加AppWidgetProviderInfo元数据小节)中的updatePeriodMillis属性定义。这个方法也在用户添加该应用部件时调用,因此它应该执行一些必要的设置,例如定义视图的事件处理和必要时创建一个临时的服务。但是,如果你声明了一个配置活动,那么在用户添加应用部件时不会调用该方法,但是它会被后续的更新调用。用户配置活动有责任在配置完成后执行首次更新。(参考上面的创建应用部件配置活动)

onAppWidgetOptionsChanged这个方法在部件被首次放置和在任何部件被重新调正大小的时候调用。你可以用这个方法来显示或隐藏部件尺寸范围内的内容。你可以同过调用getAppWidgetOptions来获得尺寸大小,这个方法返回一个包含如下的包(译者注:内存数据包bundle):

这个回调函数在API级别16引入(安卓4.1版本)。如果你实现了这个回调函数,确保你的应用不会依赖它,因为它不会在旧的设备上被调用。

onDeleted(Context, int[]) 这个方法在每次应用部件被从应用部件的宿主删除时调用。

onEnabled(Context)这个方法在应用部件的实例首次被创建时调用。例如,如果用户添加了两个你的应用部件的实例,该方法仅在第一次添加时调用。如果你需要打开一个新的数据库或执行其他需要为所有应用部件实例执行一次的设置,那么这是做这件事的好地方。

onDisabled(Context)这个方法在最后一个应用部件的实例被从应用部件宿主删除的时候调用。这是应该清理任何在onEnabled(Context)已完成工作的地方,例如删除临时数据库。

onReceive(Context, Intent)这个方法在每次广播的时候被调用并且在上述个回调方法之前。你通常不需要实现这个方法,因为默认AppWidgetProvider实现过滤了所有应用部件广播并调用上述相应的方法。

最终要的AppWidgetProvider回调方法就是onUpdate,因为它在每个应用部件被添加到应用部件宿主时调用(除非你使用配置活动)。如果你的应用部件接受了任何用户的交互事件,那么你需要在回调方法中注册事件处理方法。如果你的应用没创建临时的文件或数据库,或执行其他需要清理的工作,那么onUpdate可能是你唯一要定义的回调方法。例如,你想要一个带有点击时启动应用的应用部件,那么你可能要使用如下AppWidgetProvider的实现:

AppWidgetProvider仅定义了onUpdate方法用于定义一个启动活动的挂起意图然后使用setOnClickPendingIntent(int, PendingIntent)将它附加到应用部件的按钮上。注意,它包含了一个循环,遍历appWidgetIds中的每一个元素,这是标识这个提供者创建的每个应用部件的ID数组。 以这种方式,如果用户创建应用部件的多个实例,那么他们都会同时更新。但是, 只有一个updatePeriodMillis的周期(译者注:这里就是更新的时间间隔,原词:schedule,大意是计划的时间表,我的理解是要表达为计划的周期,这里直接省略为周期)将被应用于所有应用部件的实例。例如,如果更新周期定义为两小时,然后另一个部件的实例在第一个之后添加了一小时,那么他们都会按照第一个定义的间隔更新并且第二个更新间隔会被忽略(他们都会每两小时更新,而不是每一小时)。

注意:因为AppWidgetProvider是BroadcastReceiver的扩展,你的进程在这个回掉方法返回之后不能保证会继续运行(参考BroadcastReceiver了解更多关于广播生命周期的信息)。如果你的应用部件创建进程可能会占用几秒钟(或许在执行网页请求)并且你要想进程继续,考虑下在onUpdate方法中启动一个服务。你可以在服务中执行对应用部件的更新,而不必担心因应用部件提供者因应用无响应(ANR)错误而关闭。查阅维基字典应用部件提供者示例了解运行服务的应用部件例子。

接收应用部件广播意图

AppWidgetProvider仅是一个便利类。如果你想直接接收应用部件广播,可以实现自己的BroadcastReceiver或重新实现onReceive(Context, Intent)回掉。你需要考虑的意图如下:

创建应用部件配置活动

如果你想让用户在他或她添加新应用部件时配置设置,可以创建一个应用部件配置活动。这个活动会被应用部件宿主自动启动并且允许用户在创建时配置可用设置,例如应用部件颜色,大小,更新间隔或其他功能设置。

配置活动应该在安卓清单文件中声明一个标准的活动。但是,它会被应用部件宿主使用ACTION_APPWIDGET_CONFIGURE操作启动,因此该活动需要接受这个意图。例如:

该活动还必须在AppWidgetProviderInfo的XML文件中使用android:configureattribute属性声明(参考添加AppWidgetProviderInfo元数据)。例如,配置活动可以像这样声明:

注意:该活动使用了全路径名称空间,因为它将在你的包外面被引用。

这就是所有你要为配置活动准备的东西。现在你所需要的就是一个实在(确实存在)的活动。但是,在实现你活动时,有两点重要的事情要记住:

在下面的小节中的代码片段会看到如何从该配置返回结果和更新应用部件的例子。

从配置活动更新应用部件

当应用部件使用配置活动时,该活动有责任在配置完成后更新应用部件。你可以通过向应用部件管理者直接请求更新。

下面是正确更新应用部件和关闭配置活动过程的总结:

提示:当你的配置活动首次打开时,设置活动结果为RESULT_CANCELED,这样,在用户在完成之前退出,应用部件宿主会被通知配置被取消并且应用部件不会被添加。

参考ApiDemos中的ExampleAppWidgetConfigure.java示例类为例。

设置预览图片

安卓3.0版本引入了previewImage字段,它指定了应用部件看起来什么样的预览图片。该预览图片在部件选取器中显示给用户。如果没有提供该字段,应用部件的图标会被用于预览图片。

这显示了你将如何在XML中指定这些设置:

为了帮助创建应用部件的预览图片(在previewImage字段中指定),安卓模拟器中包含了叫“Widget Preview。”的应用。要创建预览图片,启动应用,选择你的应用的应用部件并设置你想让预览图片如何显现,然后保存并将它放在应用的可绘制资源中。

在锁屏中启用应用部件

安卓4.2版本引入了能让用户将部件添加到锁屏的能力。在指定AppWidgetProviderInfo的XML文件中声明android:widgetCategory属性,来表示可以在锁屏使用你的应用部件。这个属性包括了两个属性:"home screen"和"keyguard "。应用部件可以声明支持其中之一或全部。

默认情况下,每个应用部件都支持放置在主屏幕上,因此"home_screen"是android:widgetCategory属性的默认值。如果你的应用部件可以用于锁屏,那么添加"keyguard"值:

如果你声明部件可以同时显示在键盘锁(锁屏)和主屏上,那么你可能会想根据显示的位置定制这个部件。例如,你可以分别为锁屏和主屏定义独立的布局。下一步是在运行时检测部件的类别并做出相应的回应。你可以调用getAppWidgetOptions以获取部件选项的包来检测部件是在锁屏上还是在主屏上。返回的包会包含键OPTION_APPWIDGET_HOST_CATEGORY,它的值将是WIDGET_CATEGORY_HOME_SCREEN或WIDGET_CATEGORY_KEYGUARD中的一个。该值部件绑定的宿主决定。在应用部件提供者中,那时你可以检测部件的分类,例如:

一旦你知道了部件的分类,就可以选择读取不同的基础布局,设置不同的属性,等等。例如:

当部件位于锁屏时,你还应该用android:initialKeyguardLayout属性为你的应用部件指定一个初始布局。这与android:initialLayout属性的工作方式一样,这种方式提供了一个应用部件初始化后立刻显示的布局并可以更新该布局。

大小调整向导

当部件由锁屏托管(3)时,框架会忽略minWidth,minHeight,minResizeWidth,和minResizeHeight字段。如果部件还是个主屏部件,那么还需要这些属性因为他们仍然用于主屏幕,但是他们会在用于锁屏时被忽略掉。

锁屏部件的宽度总会填充整个提供的空间。对于锁屏部件的高度,你有如下选择:

为应用部件使用集合(5)

安卓3.0版本为应用部件引入了集合。这些应用部件类型使用RemoteViewsService来显示由远端数据支持的集合,例如来自内容提供者数据。由RemoteViewsService提供的数据显示在使用如下视图类型的应用部件中,我们将称他们为”集合视图:“

如上所述,这些视图显示了由远端数据支持的集合。这意味着他们可以使用适配器将他们的界面绑定到他们的数据上。适配器将从一组数据中把单独的条目绑定到单独的视图对象中。因为这些集合试图由适配器支持,安卓框架必须包含额外的架构以支持他们在应用部件中的使用。在应用部件的上下文中,适配器被RemoteViewsFactory替换,这是简单地围绕适配器接口的轻度包装。当请求集合中的特定条目时,RemoteViewsFactory创建并返回这个集合条目的RemoteViews对象。为了在应用部件中包含集合视图,你必须实现RemoteViewsService和RemoteViewsFactory。

RemoteViewsService是允许远端适配器请求RemoteViews对象的服务。RemoteViewsFactory是集合视图(例如,ListView,GridView等等)和它的核心数据间的适配接口。这是StackView部件示例中你用来实现服务和接口的样板代码的例子。

示例应用

本小节的代码摘录是从StackView部件示例中摘出:

android应用程序的四大组件(开发中文引导-应用小部件)(1)

这个例子有一叠10个显示从"0!"到"9!"的视图构成,这个示例应用部件有这些主要行为:

实现带有集合应用部件

要实现带有集合的应用部件,你要遵循一些将用来实现任何应用部件的基础步骤。下面的小节描述了你需要执行的另外的步骤用以实现带有的应用部件。

带有集合的应用部件的清单文件

除了在清单中声明应用部件列出的需求之外,要是带有集合视图的应用部件绑定到RemoteViewsService成为可能,你必须在清单文件中使用BIND_REMOTEVIEWS权限声明服务。这阻止了其他应用随意访问你的应用部件数据。例如,当创建使用usesRemoteViewsService填充集合视图的应用部件时,该清单项可能看起来像这样:

android:name="MyWidgetService"行引用了你的RemoteViewsService类。

带有集合的应用部件的布局

应用部件布局XML文件的主要需求就是包含这些集合视图中的一个:ListView,GridView,StackView,或AdapterViewFlipper。下面是StackView部件示例中的widget_layout.xml:

注意空视图必须是其空视图代表空状态的集合视图的相邻视图(译者注:这里是描述的是列表里没数据时,屏幕什么都没有的情况,这是可以为这类集合使用一个空视图,并列的放一起就行。这里表达的意思是只有集合需要空试图的时候才能在旁边放个空视图。例如,一个只有一个文本”请拖拽刷新“的空视图。)。

除了整个应用部件的布局文件为,你还必须创建定义了集合中每个条目布局的布局文件(例如数据集合中每本书的布局)。例如,既然所有条目都使用相同的布局,StackView 部件示例仅有一个布局文件,widget_item.xml。但是WeatherListWidget示例有两个布局文件:dark_widget_item.xml和light_widget_item.xml。

带有集合的应用部件的AppWidgetProvider类

作为一个正常的应用部件,AppWidgetProvider子类中的大部分代码通常是在onUpdate中。在创建带有集合的应用部件时,onUpdate实现的主要区别就是你必须调用setRemoteAdapter。这告诉集合视图从哪里获取数据。RemoteViewsService可以在那时返回你的RemoteViewsFactory实现,然后应用部件可以提供相应的数据。当你调用这个方法时,必须传递指向RemoteViewsService的实现和标明要更新的应用部件的应用部件ID。

例如,下面是StackView部件如何实现onUpdate方法来设置RemoteViewsService为应用部件集合的远端适配器的:

远端视图服务类

数据持久化

你依靠服务的单个示例,或任何它所包含的数据来持久化。因此,你不应该在RemoteViewsService中保存任何数据(除非它是静态的)。如果你想应用部件的数据持久化,最好的方案是使用一个数据持续超过该进程生命周期的内容提供者。

如上所述,RemoteViewsService子类要提供RemoteViewsFactory用以填充集合视图。

具体来说,你需要执行这些步骤:

RemoteViewsService实现的主要内容是它的RemoteViewsFactory,如上所述。

远端视图工厂接口

实现RemoteViewsFactory接口的自定义类要为应用部件提供集合条目的数据。为了做到这点,它结合了你的应用部件条目XML布局文件和一个数据源。这个数据源可以是从数据库到简单的数组的任何事物。在StackView部件示例中,数据源是WidgetItems的数组。RemoteViewsFactory用作连接数据与远端集合视图。

有两个你需要为RemoteViewsFactory子类实现的重要方法是onCreate和getViewAt。

当你首次创建工厂时系统调用onCreate。这是你创建任何数据源的连接和/或游标的地方。例如StackView部件示例使用onCreate初始化一个WidgetItem对象的数组。当你的应用部件激活时,系统使用他们在数组中的索引位置来访问这些对象和它们所包含要显示的文本。

下面是StackView部件示例中的RemoteViewsFactory实现的摘录,它显示了onCreate方法的部分代码:

RemoteViewsFactory的方法getViewAt返回一个对应于数据集中特定位置的数据的远端视图对象。下面是StackView部件示例中RemoteViewsFactory实现的一段摘录:

为单独条目添加行为

上面的小节为你展示了如何为应用部件集合绑定数据。但是如果你想要在集合视图中为每个单独的条目动态的行为要做什么那?

正如使用应用部件提供者类中描述的那样,你通常要使用setOnClickPendingIntent设置对象的点击行为——如引发按钮启动一个活动。但这个方案不能用于单独集合条目(举例说明,你可以使用setOnClickPendingIntent()在Gmail应用部件中建立一个全局按钮启动该应用,但不能用于每个单独的列表项)的子视图。相反,你要使用setOnClickFillInIntent来为集合中单独条目添加点击行为。这需要为集合视图建立一个挂起意图模板,然后通过RemoteViewsFactory为每个集合中的条目设置一个填充意图。

本节使用StackView部件示例来描述如何为单独条目添加行为。在StackView部件示例中,如果你点击了上面的视图,应用部件显示了Toast消息”点击了视图n,“这里的n是点击视图的索引(位置)。下面描述了它是如何工作的:

注意:StackView部件示例使用了广播,但是像这样的情况,应用部件通常会简单地启动一个活动。

建立一个挂起意图模板

StackWidgetProvider (AppWidgetProvider子类)建立一个挂起意图。单独的集合条目不能建立他们自己的挂起意图。相反,集合作为整体建立一个挂起意图模板,然后单独条目建立一个填充意图来创建一个逐条基础上的独特行为。

这个类还接收用户点击视图时发送的广播。它在onReceive方法中处理这个事件。如果意图的操作是TOAST_ACTION,那么该应用部件将为当前视图显示一个Toast消息。

建立填充意图

RemoteViewsFactory必须对每个集合条目创建一个填充意图。这使得区分单独的点击特定条目的操作成为可能。然后填充意图与挂起意图模板结合以决定点击条目时将要执行的最终意图。

保持集合数据的刷新

下面的图展示了更新发生时,使用了集合的应用部件中发生的流程。它展示了应用部件代码如何与RemoteViewsFactory交互,和你可以如何触发更新:

android应用程序的四大组件(开发中文引导-应用小部件)(2)

使用集合的应用部件的一个特征是可以提供给用户最多的最新内容的能力。例如,考虑下安卓3.0版本中的Gmail应用部件,它提供给用户他们的收件箱的快照。要使这个成为可能,你需要能触发RemoteViewsFactory和集合视图以获取和显示新的数据。通过AppWidgetManager调用notifyAppWidgetViewDataChanged来实现这点。这个方法导致一个RemoteViewsFactory的onDataSetChanged方法的回掉,它给你获取新数据的机会。注意你可以在onDataSetChanged中同步地执行密集型操作。你要保证这个调用会在元数据或视图数据被从RemoteViewsFactory获取之前完成。另外,你可以在getViewAt方法中执行密集型操作。如果这个调用占用了很长时间,加载视图(由RemoteViewsFactor的getLoadingView()视图指定)会被显示在集合视图中相应的位置,直到它返回。

译者注:这里不支持代码,所以没有显示出来。

百度首发地址:《android中文开发向导》

,