Android API Guides---App Widgets

应用小工具

应用Widget是可以在其他应用程序(如主屏幕)嵌入并接受定期更新的微型应用程序的意见。这些观点在用户界面被称为小工具,你可以发布一个与应用的Widget提供商。即能持有其他应用程序的窗口小部件的应用程序组件被称为应用程序的Widget主机。下面的截图显示音乐应用的Widget。


本文介绍了如何使用应用程序的Widget提供者发布应用的Widget。为了创建自己的AppWidgetHost托管应用小部件的讨论,请参阅应用的Widget主机。


设计的Widget
有关如何设计你的应用程序窗口小部件的信息,请阅读窗口小部件的设计指南。
基础


要创建一个应用程序窗口小部件,您需要满足以下条件:


AppWidgetProviderInfo对象
描述元数据为应用的Widget,如在App Widget的布局,更新频率,以及AppWidgetProvider类。这应当以XML来定义。
AppWidgetProvider类的实现
定义的基本方法,让您以编程与应用程序widget界面,基于广播的事件。通过它,当应用程序的Widget被更新,启用,禁用和删除您将收到广播。
视图布局
定义了应用程序插件的初始布局,在XML中定义。
此外,还可以实现一个应用的Widget配置活动。这是一个可选的活动,当用户将您的Widget应用程序,并允许他或她在修改创作时应用小工具设置,启动。


以下各节描述了如何设置这些组件。


在清单中声明一个应用程序窗口小部件


首先,声明在应用程序的Andr​​oidManifest.xml文件中的AppWidgetProvider类。例如:

<receiver android:name="ExampleAppWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>
在<接收器>元素需要在android:name属性,它指定了应用程序的Widget使用的AppWidgetProvider。


在<意向filter>元素必须包含了Android的<action>元素:name属性。该属性指定AppWidgetProvider接受ACTION_APPWIDGET_UPDATE播出。这是你必须显式声明的唯一播出。该AppWidgetManager自动发送所有其他App的Widget广播到AppWidgetProvider是必要的。


在<元数据>元素指定AppWidgetProviderInfo资源,需要以下属性:


机器人:名字 - 指定的元数据名称。使用android.appwidget.provider标识数据作为AppWidgetProviderInfo描述符。
机器人:资源 - 指定AppWidgetProviderInfo资源位置。
添加AppWidgetProviderInfo元


该AppWidgetProviderInfo一个App Widget时,基本素质定义,如最小尺寸的布局,其初始布局资源,多久更新应用程序窗口小部件,和(可选)配置活动在创建时推出。定义AppWidgetProviderInfo对象使用一个<appwidget提供商>元素的XML资源,并将其保存在项目的RES / XML /文件夹。


例如:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:updatePeriodMillis="86400000"
    android:previewImage="@drawable/preview"
    android:initialLayout="@layout/example_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen">
</appwidget-provider>
这里的<appwidget提供商>的摘要属性:


对了minWidth和属性了minHeight的值指定应用的Widget消耗默认的最小空间量。默认主页屏幕位置应用小部件在其窗口的基础上具有一个定义的高度和宽度的细胞的网格。如果一个App Widget的最小宽度或高度值不匹配的单元格的尺寸,然后应用程序窗口小部件的尺寸四舍五入到最接近的单元尺寸。
查看应用程序的Widget设计准则有关调整你的App小工具的更多信息。
注:为了使您的应用程序窗口小部件可跨设备,你的应用程序widget的最小尺寸不应大于4×4细胞。


该minResizeWidth和minResizeHeight属性指定应用Widget的绝对最小尺寸。这些值应指定大小低于该应用的Widget会难以辨认或不可用。使用这些属性允许用户小窗口调整大小的尺寸可能比由了minWidth和了minHeight属性定义的默认插件尺寸。在推出的Andr​​oid 3.1。
查看应用程序的Widget设计准则有关调整你的App小工具的更多信息。
该updatePeriodMillis属性定义多久的Widget应用程序框架应通过调用的onUpdate()回调方法要求从AppWidgetProvider更新。实际更新不保证准确地出现在时间这个值,我们则建议尽可能少,也许不超过一小时一次,以节省电池。您可能还允许用户调整的频率配置,有些人可能希望有一个股票代码,以每15分钟,或者也许只有一天四次更新。
注意:如果该装置处于睡眠状态时,它是时间的更新(如由updatePeriodMillis定义),则该设备将依次执行更新醒来。如果不更新超过每小时一次,这大概不会引起对电池寿命显著问题。但是,如果您需要更新更频繁和/或你并不需要更新,而设备是睡着了,那么你就可以执行,而不是基于不会唤醒设备的报警的更新。要做到这一点,设置闹钟与你的AppWidgetProvider接收,使用AlarmManager的意图。设置报警类型要么ELAPSED_REALTIME或RTC,其中,当该设备是醒着将仅提供警报。然后设置updatePeriodMillis为零(“0”)。


该initialLayout属性点来定义应用程序的Widget布局布局资源。
该配置属性定义了活动,当用户添加应用程序窗口小部件,为了推出他或她来配置应用程序窗口小部件的属性。这是可选的(阅读下面创建一个应用程序窗口小部件配置活动)。
该previewImage属性指定的应用程序部件将是什么它的配置后的预览,选择应用程序窗口小部件时,用户看到。如果没有提供,用户,而不是看到你的应用程序的启动图标。该字段对应于Android的:在AndroidManifest.xml文件的<接收>元素previewImage属性。对于使用previewImage的更多讨论,请参阅设置预览图像。介绍了Android 3.0的。
该autoAdvanceViewId属性指定的应用程序部件子视图应该是自动先进的部件的主机的视图ID。介绍了Android 3.0的。
该resizeMode属性指定由一个widget可以调整的规则。您可以使用此属性使主屏幕小部件可调整大小,水平,垂直或两个轴。用户触摸按住某个小部件,以显示其缩放柄,然后拖动水平和/或垂直手柄来改变布局网格的大小。对于resizeMode属性的值包括“水平”,“垂直”和“无”。为调整大小水平和垂直声明一个小部件,提供值“横|纵”。在推出的Andr​​oid 3.1。
该minResizeHeight属性指定该控件可以调整的最低高度(DPS)。此字段没有任何影响,如果超过了minHeight大于或没有启用垂直大小调整(见resizeMode)。介绍了Android 4.0的。
该minResizeWidth属性指定了小部件可以调整大小的最小宽度(DPS)。此字段没有任何影响,如果超过了minWidth大于或没​​有启用水平调整大小(见resizeMode)。介绍了Android 4.0的。
该widgetCategory属性声明是否你的应用程序的Widget可以在主屏幕(home_screen),锁屏(键盘保护),或者两者上显示。只有Android的版本比5.0的支持锁屏小部件低。对于Android 5.0以上版本,只有home_screen是有效的。
见AppWidgetProviderInfo类由<appwidget-provider>元素接受属性的更多信息。


创建应用程序窗口小部件布局


你必须在XML定义一个初始布局为您的应用程序窗口小部件,并将其保存在项目的RES /布局/目录下。您可以使用下面列出的视图对象设计的App窗口小部件,但你开始设计的Widget应用程序之前,请阅读并理解应用的Widget设计指南。


创建应用程序窗口小部件的布局很简单,如果你熟悉的布局。但是,你必须要知道,应用程序的Widget布局是基于RemoteViews,这并不支持每一种布局或视图小部件。


一个RemoteViews对象(,因此,应用程序的Widget),可以支持以下布局类:


的FrameLayout
的LinearLayout
的RelativeLayout
网格布局
而下面的小部件类:


AnalogClock
按键
时计
的ImageButton
ImageView的
进度条
TextView中
ViewFlipper
列表显示
网格视图
StackView
AdapterViewFlipper
不支持这些类的后裔。


RemoteViews还支持ViewStub,这是一种无形的,零大小的视图,您可以使用在运行时懒洋洋地膨胀布局资源。


添加边距应用小工具


窗口小部件,一般不应延伸到屏幕边缘,不应该在视觉上与其他小部件齐平,所以你应该在你的周围小窗口框各方增加利润。


由于Android 4.0,应用小部件会自动给出的小窗口框和应用程序窗口小部件的边框为用户提供用户的主屏幕上的其他部件和图标更好地协调之间的填充。要充分利用这一强烈推荐行为的优势,设置应用程序的targetSdkVersion至14或更大。


这很容易写出具有适用于早期版本的平台,自定义边距单一的布局,并有Android 4.0及更高没有多余的边距:


设置应用程序的targetSdkVersion至14或更大。
创建一个布局,如下面的人,引用其利润率方面的资源:

<FrameLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:padding="@dimen/widget_margin">

  <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="@drawable/my_widget_background">
    …
  </LinearLayout>

</FrameLayout>
创建两个维度的资源,一个在RES/价值/提供预Android 4.0的定制空间,和一个在res/值-V14/提供Android 4.0的小部件没有微胖:
RES/价值/ dimens.xml:

<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml :

<dimen name="widget_margin">0dp</dimen>
另一种选择是简单地在默认情况下建造额外的利润到您的九宫背景的资产,并提供不同的九补丁没有利润率API级别14或更高版本。


使用AppWidgetProvider类


您必须声明你的AppWidgetProvider类实现为使用在AndroidManifest的<接收器>元素的广播接收器(参见清单声明一个应用的Widget上面)。
在AppWidgetProvider类广播接收器的方便类来处理应用程序的Widget广播延伸。在AppWidgetProvider只接收事件广播是相关的在App控件,当应用程序的Widget被更新,删除,启用和禁用等。当这些广播事件发生时,AppWidgetProvider收到以下的方法调用:


的onUpdate()
这就是所谓的更新应用程序的Widget在由AppWidgetProviderInfo的updatePeriodMillis属性定义的时间间隔(见添加AppWidgetProviderInfo元以上)。当用户添加应用的Widget这种方法也被称为,所以应该在必要时进行必要的设置,如定义事件处理程序的视图和启动一个临时服务。但是,如果你已经声明了一个配置活动,是不是当用户添加应用程序窗口小部件,但呼吁后续更新调用此方法。它是配置活动时,配置完成后进行第一次更新的责任。 (请参阅下面创建一个应用程序窗口小部件配置活动。)
onAppWidgetOptionsChanged()
当小部件先放入这就是所谓的任何时间小部件大小。您可以使用此回调的基础上的物件的尺寸范围,以显示或隐藏的内容。您可以通过调用getAppWidgetOptions(),它返回一个包,其中包括以下获得尺寸范围:


OPTION_APPWIDGET_MIN_WIDTH - 包含下界当前宽度,以dp为单位,一个widget实例。
OPTION_APPWIDGET_MIN_HEIGHT - 包含下界当前高度,以dp为单位,一个widget实例。
OPTION_APPWIDGET_MAX_WIDTH - 包含的上限在当前宽度,以dp为单位,一个widget实例。
OPTION_APPWIDGET_MAX_HEIGHT - 包含的上限在当前宽度,以dp为单位,一个widget实例。
这个回调是在API级别16(是Android 4.1)出台。如果实施此回调,请确保您的应用程序不依赖于它,因为它不会对旧设备进行调用。
onDeleted(上下文,INT [])
这就是所谓的每一个App的Widget从应用的Widget主机中删除的时间。
onEnabled(上下文)
当一个实例的应用程序的widget被首次创造了这个被调用。例如,如果用户将您的Widget应用程序的两个实例,这只是所谓的第一次。如果您需要打开一个新的数据库或进行其他设置只需要为所有App控件实例出现一次,那么这是一个很好的地方去做。
onDisabled(上下文)
当你的应用的Widget的最后一个实例是从应用的Widget主机中删除这就是所谓的。这是你应该清理onEnabled(上下文)所做的任何工作,如删除临时数据库。
的onReceive(上下文,意图)
这被称为用于每个广播和上述各回调方法之前。你通常不需要,因为默认的AppWidgetProvider实现过滤所有App的Widget广播并调用上面的方法适当实现此方法。
最重要的AppWidgetProvider回调的onUpdate(),因为当每个应用程序窗口小部件添加到主机(除非你使用一个配置Activity)的叫法。如果你的应用程序的Widget接受任何用户交互事件,那么你需要在这个回调注册事件处理程序。如果你的应用的Widget不创建临时文件或数据库,或执行需要清理,然后其他的onUpdate工作()可能是你需要定义的唯一回调方法。例如,如果你想要一个应用程序widget工具点击时启动一个活动的一个按钮,你可以使用下面的实现AppWidgetProvider的:

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);

            // Get the layout for the App Widget and attach an on-click listener
            // to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}
这AppWidgetProvider定义了用于定义启动一个活动的PendingIntent,并与setOnClickPendingIntent(INT,的PendingIntent)连接到App Widget的按钮的目的只有的onUpdate()方法。请注意,它包含一个循环,通过appWidgetIds每个条目,这是确定此提供程序创建的每个应用程序的Widget ID数组进行迭代。以这种方式,如果用户创建的应用程序的widget的多个实例,则它们都同时更新。但是,只有一个updatePeriodMillis时间表将用于应用程序的widget的所有实例进行管理。例如,如果更新时间表被定义为每两个小时,和App小工具的第二个实例是第一个之后添加一小时后,他们都将在由第一和第二更新所定义的周期来更新期间将被忽略(他们都会进行每两小时不每小时更新一次)。


注意:因为AppWidgetProvider是广播接收器的扩展,你的进程不能保证继续运行,回调方法返回后(见的BroadcastReceiver关于广播生命周期信息)。如果你的应用的Widget安装过程可能需要几秒钟(也许当执行网页请求),你需要你的进程继续,考虑在的onUpdate()方法启动服务。从服务中,您可以不用担心AppWidgetProvider收盘下跌,由于应用程序执行自己的更新到App控件不响应(ANR)错误。参见维基样品的AppWidgetProvider运行服务一个App的Widget的例子。


另请参阅ExampleAppWidgetProvider.java示例类。


接收应用的Widget广播意图


AppWidgetProvider只是一个方便的类。如果您想直接接收应用的Widget广播,你可以实现自己的BroadcastReceiver或者重写的onReceive(上下文,意图)回调。你需要关心的意图如下:


ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
创建应用程序窗口小部件配置活动


如果您想用户配置设置时,他或她增加了一个新的Widget应用程序,你可以创建一个应用程序的Widget配置活动。本次活动将通过应用程序的Widget主机自动启动,并允许用户在配置创建时间为App的Widget可用的设置,如应用程序的Widget颜色,尺寸,更新周期或其他功能的设置。


配置活动应该被声明为Android清单文件中的正常活动。但是,它将被应用的Widget主机与ACTION_APPWIDGET_CONFIGURE行动推出,所以活动需要接受这个意图。例如

<activity android:name=".ExampleAppWidgetConfigure">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
    </intent-filter>
</activity>
(见添加AppWidgetProviderInfo元以上)配置属性:此外,活动必须在AppWidgetProviderInfo XML文件中声明,与Android。例如,配置活动可以声明如下:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:configure="com.example.android.ExampleAppWidgetConfigure" 
    ... >
</appwidget-provider>
请注意,该活动是声明的完全限定的命名空间,因为它会从你的包范围内引用。
这就是所有你需要开始使用的配置活动。现在,所有你需要的是实际的活动。有,但是,当你实现了活动的两个重要的事情要记住:
该应用程序的Widget宿主调用配置活动以及配置活动应该总是返回一个结果。结果应该包括由发起的活动(保存在Intent作为额外EXTRA APPWIDGETID)的意图传递的应用程序窗口小部件ID。
在创建应用程序窗口小部件时(当配置活动推出的系统将不会发送ACTION APPWIDGET_UPDATE广播)的的onUpdate()方法将不会被调用。它是配置活动的责任从AppWidgetManager请求更新第一次创建应用程序窗口小部件时。然而,的onUpdate()将被要求对后续更新,它只是跳过第一次。
请参阅下一节的代码片段如何从配置返回结果和更新应用的Widget的例子。
从配置活动更新应用的Widget
当一个应用程序的Widget使用配置的活动,这是活动的责任来更新应用程序窗口小部件时配置完成。您可以直接从AppWidgetManager请求更新这么做。
下面就来正确地更新应用程序窗口小部件并关闭配置活动的程序的摘要:
首先,获得从发射活动的意图应用的Widget ID:

Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
    mAppWidgetId = extras.getInt(
            AppWidgetManager.EXTRA_APPWIDGET_ID, 
            AppWidgetManager.INVALID_APPWIDGET_ID);
}
执行你的App小部件配置。
当配置完成后,通过调用的getInstance(上下文)获得AppWidgetManager的实例:

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
Update the App Widget with a  RemoteViews  layout by calling  updateAppWidget(int, RemoteViews) :

RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
  1. Finally, create the return Intent, set it with the Activity result, and finish the Activity:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
提示:当您的配置活动第一次打开,设置活动结果RESULT_CANCELED。这样一来,如果用户备份出活动的到达结束之前,应用的Widget通知主机的配置被取消和App的Widget将不能添加。


见ApiDemos的ExampleAppWidgetConfigure.java示例类的例子。


设置预览图像


Android 3.0的推出previewImage领域,它指定什么样的应用程序窗口小部件看起来像一个预览。此预览被示出为从微件选择器的用户。如果没有提供这个领域,应用Widget的图标用于预览。


这是你如何指定XML此设置

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:previewImage="@drawable/preview">
</appwidget-provider>
为了帮助您的应用程序窗口小部件(指定预览图像场)创建一个预览图像,Android模拟器包括称为应用程序“窗口小部件预览”。要创建一个预览图像,启动这个应用程序,选择您的应用程序应用程序窗口小部件,并将它设置你喜欢你们的预览图像出现,然后将其保存,并将其放置在应用程序的绘图资源。
使用App窗口小部件与收藏
Android 3.0的推出应用小部件与集合。这些种类的应用程序的窗口小部件的使用RemoteViewsService来显示由遥控数据,如从内容提供商备份集合。由RemoteViewsService提供的数据呈现在使用下面的视图类型,我们将把为一体的应用程序部件“收集的意见:”
列表显示
这显示了垂直滚动列表项的视图。举一个例子,看看Gmail应用程序部件。
网格视图
这显示了在二维滚动网格项目的视图。举一个例子,看到书签应用部件。
StackView
一种堆叠卡片视图(有点像一个关系网,),在这里用户可以轻弹前卡上/下看到一个/下一个卡,分别为。例子包括YouTube和图书应用的部件。
AdapterViewFlipper
适配器支持简单ViewAnimator两个或多个视图之间动画。只有一个孩子,显示在一段时间。
如上所述,这些收集的意见显示由远程数据支持的集合。这意味着,他们使用适配器将其用户界面绑定到他们的数据。适配器结合从一组数据成单个视图对象的单个项目。由于这些收集的意见是通过适配器支持,Android框架必须包括额外的架构,支持应用小部件的使用。在一个应用程序widget的背景下,适配器是由RemoteViewsFactory,这简直就是围绕适配器接口的瘦包装所取代。当集合中要求特定项目时,RemoteViewsFactory创建并返回该项目的集合作为一个RemoteViews对象。为了在您的应用程序窗口小部件集合视图,您必须实现RemoteViewsService和RemoteViewsFactory。
RemoteViewsService是一种服务,它允许远程适配器请求RemoteViews对象。 RemoteViewsFactory是一个集合视图之间的适配器,该视图的基础数据的接口(如ListView中,GridView的,等等)和。从StackView的Widget样本,这里是你用来实现这个服务和接口的样板代码的例子:

public class StackWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {

//... include adapter-like methods here. See the StackView Widget sample.

}
示例应用程序
本节中的代码摘录从StackView控件样品得出:

Android API Guides---App Widgets_第1张图片

该样本包括10次,其中显示值栈的“0”!到“9”!示例应用程序部件有这些主要行为:


用户可以垂直甩在应用程序插件的顶视图来显示一个或下一个视图。这是一个内置StackView行为。
无需任何用户交互,应用小工具会自动通过其在序列意见的进步,像幻灯片。这是由于设置的android:在res / XML / stackwidgetinfo.xml文件autoAdvanceViewId =“@ ID / stack_view”。此设置适用于该视图的ID,在这种情况下是堆栈视图的视图ID。
如果用户触摸的顶视图,该应用插件播放吐司消息“已触摸视图N,”,其中n是被触摸的视图的索引(位置)。对于这是如何实现的更多讨论,请参见添加行为的个别项目。
实施与应用的集合小工具


为了实现与集合的应用程序插件,你按照你会用它来实现任何应用程序插件的相同的基本步骤。以下各节介绍您需要执行来实现与集合的应用程序插件的额外步骤。


清单与集合应用小工具


除了声明中的清单应用程序窗口小部件列出的要求,使其能够用于收藏的应用程序部件绑定到你的RemoteViewsService,您必须在使用许可BIND_REMOTEVIEWS您的清单文件中声明该服务。这可以防止其他应用程序访问任意你的应用程序widget的数据。例如,创建一个使用RemoteViewsService来填充集合视图一个App窗口小部件时,清单条目可能是这样的:

<service android:name="MyWidgetService"
...
android:permission="android.permission.BIND_REMOTEVIEWS" />
该生产线机器人:名字=“MyWidgetService”指的是你RemoteViewsService的子类。
布局与集合应用小工具
为您的应用程序部件的布局XML文件的主要要求是,它包括收集意见之一:ListView中,GridView控件,StackView或AdapterViewFlipper。下面是StackView样品的Widget小部件layout.xml:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <StackView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/stack_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:loopViews="true" />
    <TextView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/empty_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:background="@drawable/widget_item_background"
        android:textColor="#ffffff"
        android:textStyle="bold"
        android:text="@string/empty_view_text"
        android:textSize="20sp" />
</FrameLayout>
需要注意的是空的意见必须是其空视图表示空状态集合视图的兄弟姐妹。


除了布局文件为您的整个应用程序窗口小部件,您必须创建定义布局集合中的每个项目另一个布局文件(例如,每本书的藏书在布局)。例如,StackView的Widget样品仅具有一个布局文件,widget_item.xml,由于所有的项目使用相同的布局。但是WeatherListWidget样品有两个布局文件:dark_widget_item.xml和light_widget_item.xml。


对于集合应用小部件AppWidgetProvider类


与普通的应用小工具,在你的AppWidgetProvider子类的大量代码通常进去的onUpdate()。在实施的onUpdate的主要区别创建带有集合一个应用程序窗口小部件时()是你必须调用setRemoteAdapter()。这告诉集合视图从哪里得到它的数据。然后RemoteViewsService可以回到你的RemoteViewsFactory实施,和小部件能容纳相应的数据。当你调用这个方法,你必须通过一个指向您的实现RemoteViewsService,并指定应用程序窗口小部件更新的应用小部件ID的意图。


例如,这里的StackView的Widget样本是如何实现的onUpdate()回调方法来设置RemoteViewsService为应用程序部件集合远程适配器:

public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
    // update each of the app widgets with the remote adapter
    for (int i = 0; i < appWidgetIds.length; ++i) {
        
        // Set up the intent that starts the StackViewService, which will
        // provide the views for this collection.
        Intent intent = new Intent(context, StackWidgetService.class);
        // Add the app widget ID to the intent extras.
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
        intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
        // Instantiate the RemoteViews object for the app widget layout.
        RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
        // Set up the RemoteViews object to use a RemoteViews adapter. 
        // This adapter connects
        // to a RemoteViewsService  through the specified intent.
        // This is how you populate the data.
        rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
        
        // The empty view is displayed when the collection has no items. 
        // It should be in the same layout used to instantiate the RemoteViews
        // object above.
        rv.setEmptyView(R.id.stack_view, R.id.empty_view);

        //
        // Do additional processing specific to this app widget...
        //
        
        appWidgetManager.updateAppWidget(appWidgetIds[i], rv);   
    }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
}
RemoteViewsService类


坚持数据


你不能靠你的服务,或者包含任何数据的单个实例,坚持。因此,应该没有任何数据存储在您的RemoteViewsService(除非它是静态的)。如果你希望你的应用程序widget的数据依然存在,最好的办法是使用的ContentProvider其数据持续超过流程生命周期。
如上所述,你RemoteViewsService子类提供用于填充远程集合视图的RemoteViewsFactory。


具体来说,您需要执行以下步骤:


子类RemoteViewsService。 RemoteViewsService是通过一个远程适配器可以请求RemoteViews服务。
在RemoteViewsService子类,包括实现RemoteViewsFactory接口的类。 RemoteViewsFactory为远程集合视图之间的适配器该视图的基础数据的接口(如ListView中,GridView的,等等),并。你的实现是负责制定一个RemoteViews对象数据集中的每个项目。这个接口是围绕适配器瘦包装。
所述RemoteViewsService执行情况的主要内容是其RemoteViewsFactory,如下所述。


RemoteViewsFactory接口


实现RemoteViewsFactory界面自定义类提供了其集合中的项目的数据应用程序部件。要做到这一点,它结合了您的应用程序部件项目XML布局文件与数据源。数据的这个源可以是来自一个数据库的任何一个简单阵列。在StackView的Widget样本,所述数据源是WidgetItems的阵列。该RemoteViewsFactory用作适配器连接到数据胶水远程采集视图。


你需要实现你的RemoteViewsFactory子类的两个最重要的方法是的onCreate()和getViewAt()。


系统调用的onCreate创建工厂的第一次时,()。这是你设置的任何连接和/或光标到数据源。例如,StackView的Widget示例使用的onCreate()来初始化WidgetItem对象的数组。当你的应用程序widget是积极的,系统访问这些对象使用数组和它们所包含的文本中的索引位置显示


下面是从StackView的Widget样品的RemoteViewsFactory实施,显示了onCreate()方法的部分摘录:

class StackRemoteViewsFactory implements
RemoteViewsService.RemoteViewsFactory {
    private static final int mCount = 10;
    private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
    private Context mContext;
    private int mAppWidgetId;

    public StackRemoteViewsFactory(Context context, Intent intent) {
        mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    public void onCreate() {
        // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,
        // for example downloading or creating content etc, should be deferred to onDataSetChanged()
        // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
        for (int i = 0; i < mCount; i++) {
            mWidgetItems.add(new WidgetItem(i + "!"));
        }
        ...
    }
...
所述RemoteViews工厂方法getView()返回在数据集中的指定位置相对应的数据的RemoteViews对象。下面是从StackView的Widget样品的RemoteViewsFactory实现的摘录:

public RemoteViews getViewAt(int position) {
   
    // Construct a remote views item based on the app widget item XML file, 
    // and set the text based on the position.
    RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
    rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);

    ...
    // Return the remote views object.
    return rv;
}
添加行为,个别项目


上述部分介绍如何将数据绑定到你的应用程序窗口小部件的集合。但是,如果你想做的动态行为添加到您的收藏视图中的单个项目?


如使用AppWidgetProvider类描述,通常使用setOnClickPendingIntent()来设置对象的点击行为,如造成一个按钮启动一个活动。但是,这种做法是不允许在单独的一个集合项目​​子视图(澄清,你可以使用setOnClickPendingIntent()设置在启动应用程序,例如Gmail应用程序部件一个全球性的按钮,但不是各列表项)。相反,添加一个集合中点击行为向单个项目,您使用setOnClickFillInIntent()。这就需要建立一个未决的意图模板您的收藏视图,然后对每个项目集合中通过您的RemoteViewsFactory设置填充的意图。


本节使用的Widget StackView样本来描述如何添加行为的个别项目。在StackView的Widget样品,如果用户触摸的顶视图,该应用插件播放吐司消息“已触摸视图N,”,其中n是被触摸的视图的索引(位置)。这是它的工作原理:


该StackWidgetProvider(一个AppWidgetProvider子类)创建待意图有一个名为TOAST_ACTION自定义操作。
当用户触摸视图,意图被触发,它广播TOAST_ACTION。
此广播是由StackWidgetProvider的的onReceive()方法拦截,以及应用程序插件播放了感动视图敬酒消息。对于集合的项目中的数据是由RemoteViewsFactory经由RemoteViewsService提供。
注:StackView的Widget示例使用广播,但通常一个应用程序窗口小部件只会在类似这样的情况下推出的一项活动。


设置挂起意图模板


该StackWidgetProvider(AppWidgetProvider子类)建立了一个悬而未决的意图。集合个人物品不能建立自己的未决意图。取而代之的是,集合作为一个整体设置了一个待处理的意图模板,并且单个项目设置填充在意图上创建一个项目逐个项目基础的独特的行为。


这个类还接收当用户触摸一个观点,即发送广播。它在它的onR​​eceive()方法处理此事件。如果意图的行动是TOAST_ACTION,应用程序插件播放当前视图敬酒消息

public class StackWidgetProvider extends AppWidgetProvider {
    public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION";
    public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM";

    ...

    // Called when the BroadcastReceiver receives an Intent broadcast.
    // Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget 
    // displays a Toast message for the current item.
    @Override
    public void onReceive(Context context, Intent intent) {
        AppWidgetManager mgr = AppWidgetManager.getInstance(context);
        if (intent.getAction().equals(TOAST_ACTION)) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
            int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0);
            Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show();
        }
        super.onReceive(context, intent);
    }
    
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // update each of the app widgets with the remote adapter
        for (int i = 0; i < appWidgetIds.length; ++i) {
    
            // Sets up the intent that points to the StackViewService that will
            // provide the views for this collection.
            Intent intent = new Intent(context, StackWidgetService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            // When intents are compared, the extras are ignored, so we need to embed the extras
            // into the data so that the extras will not be ignored.
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
            rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);
    
            // The empty view is displayed when the collection has no items. It should be a sibling
            // of the collection view.
            rv.setEmptyView(R.id.stack_view, R.id.empty_view);

            // This section makes it possible for items to have individualized behavior.
            // It does this by setting up a pending intent template. Individuals items of a collection
            // cannot set up their own pending intents. Instead, the collection as a whole sets
            // up a pending intent template, and the individual items set a fillInIntent
            // to create unique behavior on an item-by-item basis.
            Intent toastIntent = new Intent(context, StackWidgetProvider.class);
            // Set the action for the intent.
            // When the user touches a particular view, it will have the effect of
            // broadcasting TOAST_ACTION.
            toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);
            toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
            intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
            PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
            rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);
            
            appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
        }
    super.onUpdate(context, appWidgetManager, appWidgetIds);
    }
}
设置填充式意图


您RemoteViewsFactory必须在集合中的每个项目设定一个填空题意图。这使得可以区分个体上点击特定项目的动作。在填充在意图然后,以确定被点击的项目时,将要执行的最终意图与的PendingIntent模板相结合。

public class StackWidgetService extends RemoteViewsService {
    @Override
    public RemoteViewsFactory onGetViewFactory(Intent intent) {
        return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
    }
}

class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
    private static final int mCount = 10;
    private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();
    private Context mContext;
    private int mAppWidgetId;

    public StackRemoteViewsFactory(Context context, Intent intent) {
        mContext = context;
        mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
                AppWidgetManager.INVALID_APPWIDGET_ID);
    }

    // Initialize the data set.
        public void onCreate() {
            // In onCreate() you set up any connections / cursors to your data source. Heavy lifting,
            // for example downloading or creating content etc, should be deferred to onDataSetChanged()
            // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.
            for (int i = 0; i < mCount; i++) {
                mWidgetItems.add(new WidgetItem(i + "!"));
            }
           ...
        }
        ...
    
        // Given the position (index) of a WidgetItem in the array, use the item's text value in 
        // combination with the app widget item XML file to construct a RemoteViews object.
        public RemoteViews getViewAt(int position) {
            // position will always range from 0 to getCount() - 1.
    
            // Construct a RemoteViews item based on the app widget item XML file, and set the
            // text based on the position.
            RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);
            rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);
    
            // Next, set a fill-intent, which will be used to fill in the pending intent template
            // that is set on the collection view in StackWidgetProvider.
            Bundle extras = new Bundle();
            extras.putInt(StackWidgetProvider.EXTRA_ITEM, position);
            Intent fillInIntent = new Intent();
            fillInIntent.putExtras(extras);
            // Make it possible to distinguish the individual on-click
            // action of a given item
            rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
        
            ...
        
            // Return the RemoteViews object.
            return rv;
        }
    ...
    }
保持收集数据新鲜


下图说明了发生在更新时使用集合的应用程序插件的流量。它显示了应用程序部件代码如何与RemoteViewsFactory互动,以及如何触发更新

Android API Guides---App Widgets_第2张图片

使用集合应用小部件的一个特点是,为用户提供了最新的内容。例如,考虑到Android 3.0的Gmail应用小工具,它为用户提供他们的收件箱的快照。要做到这一点,你需要能够触发RemoteViewsFactory和收集,以获取并显示新的数据。您的通话AppWidgetManager实现这一notifyAppWidgetViewDataChanged()。此调用生成一个回调到您RemoteViewsFactory的onDataSetChanged()方法,它让你有机会获取任何新数据。请注意,您可以内onDataSetChanged()回调同步执行处理密集型操作。你保证,之前的元数据或显示数据是从RemoteViewsFactory获取此调用将完成。此外,可以将getViewAt()方法中进行处理密集型操作。如果该调用需要很长的时间,加载视图(由RemoteViewsFactory的getLoadingView()方法指定),将显示在集合视图,直到它返回的相应位置。




你可能感兴趣的:(android,api,sdk,阅读,谷歌)