App Widget详解

App Widget是一种可以被放在其他应用中(如Launcher)并接收周期性更新的应用视图。这些视图在UI上就表现为Widget,并且你可以同App Widget Provider一起发布。

对于能够包含其他App Widget的应用程序组件,称为App Widget Host

基本信息

要创建一个App Widget,你需要完成以下步骤:

lAppWidgetProviderInfo对象:它描述了App Widget的基本元素,比如说布局、更新频率、AppWidgetProvider类等。这些都是在xml文件中定义的。

l  AppWidgetProvider类的实现:它定义了一些基本的方法以支持通过广播事件与App Widget交互。通过它,当App Widget被更新、启用、禁用以及删除时,你将收到相应的广播信息。

l  View Layout:通过xml文件定义App Widget的初始视图。

另外,你还可以实现一个App Widget的配置Activity。当然,这不是强制的。

AndroidManifest中声明一个App Widget

首先,声明AppWidgetProvider

 

Xml代码  收藏代码

  1. <receiver android:name="ExampleAppWidgetProvider" >  

  2.     <intent-filter>  

  3.         <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />  

  4.     </intent-filter>  

  5.     <meta-data android:name="android.appwidget.provider"  

  6.                android:resource="@xml/example_appwidget_info" />  

  7. </receiver>  

 

<receiver>标签用于指明App Widget使用的AppWidgetProvider

<intent-filter>标签必须包括一个含有android:name属性的<action>标签。该属性用于指明AppWidgetProvider接收APPWIDGET_UPDATE广播。这是你唯一需要显示声明的广播。当有需要时,AppWidgetManager自动发送AppWidgetProder所需的各种广播。

<meta-data>标签标识了AppWidgetProviderInfo资源,它需要以下属性:

  l  android:name:使用android.appwidget.provider来标识AppWidgetProviderInfo

  l  android:resource:标识AppWidgetProviderInfo的资源位置。

添加AppWidgetProviderInfo元数据

AppWidgetProviderInfo定义了一个App Widget的必要属性,例如最小布局范围、初始布局、更新频率、以及在创建时显示的配置Activity(可选)。

AppWidgetProviderInfo使用<appwidget-provider>标签来定义,并保存在res/xml文件夹中。

Xml代码  收藏代码

  1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  

  2.     android:minWidth="294dp"  

  3.     android:minHeight="72dp"  

  4.     android:updatePeriodMillis="86400000"  

  5.     android:previewImage="@drawable/preview"  

  6.     android:initialLayout="@layout/example_appwidget"  

  7.     android:configure="com.example.android.ExampleAppWidgetConfigure"   

  8.     android:resizeMode="horizontal|vertical">  

  9. </appwidget-provider>  

 l minWidthminHeight属性表示了App Widget所需的最小布局区域。

默认的主屏中,App Widget要想确认其位置,需要通过基于网格的具有固定宽度和高度的单元。如果App Widget的最小宽度和高度无法匹配给定的单元,它将会自动扩展到最接近的单元大小。

由于主屏的布局方向是可变的,你应该考虑最坏的情况(每单元的宽和高都是74dp)。然而,为了防止在扩展时产生整数计算错误,你还需要减去2。因此,你可以用以下公式来计算最小宽度和高度(单位dp):(单元数量×74-2

同时,为了保证你的App Widget能够在各种设备上正常使用,它们的宽度和高度必须不超过4×4个单元。

        lupdatePeriodMillis属性定义了App Widget框架调用AppWidgetProvideronUpdate方法的频率。对于实际的更新,我们不建议采用该值进行实时处理。最好是越不频繁越好——为了保证电量,一小时不超过一次为好。当然,你也可以允许用户对更新频率进行设置。

注意,如果更新触发时设备正处于休眠状态,设备将唤醒以执行该操作。如果你的更新频率不超过一小时一次,这不会对电池的寿命产生多大的影响。但如果你需要更频繁地更新却又不想要在设备休眠时执行,那你可以使用定时器来执行更新。要达到这种目的,可以在AlarmManager中设置一个AppWidgetProvider能接收的Intent。将类型设为ELAPSED_REALTIMERTC。由于AlarmManager只有当设备处于唤醒状态时才会被调用,我们只要设updatePeriodMillis0即可。

        linitialLayout属性标识了初始布局文件。

        lconfigure属性定义了当用户添加App Widget时调用的Activity。(这是可选的)

        lpreviewImage定义了App Widget的缩略图,当用户从widget列表中选择时,显示的就是这张图。如果没设置,用户将看见的是你的应用的默认图标。

        lautoAdvanceViewId属性是在Android3.0引入的,用于标识需要被hostlauncher)自动更新的widget的子视图。

        l resizeMode属性标识了widget重新布局的规则。你可以使用该属性来让widget能够在水平、竖直、或两个方向上均可变化。可用的值包括horizontalverticalnone。如果是想在两个方向上均能拉伸,可设置为horizontal|vertical,当然,需要Android3.1以上版本。

创建App Widget的布局

要创建你的App Widget的初始布局,你可以使用以下View对象。

创建布局不是很麻烦,重点是,你必须记住,这个布局是基于RemoteViews的,不是所有的布局类型与View都支持。

一个RemoteViews对象可以支持以下布局类:

FrameLayout

LinearLayout

RelativeLayout

以及一下widget

AnalogClock

Button

Chronometer

ImageButton

ImageView

ProgressBar

TextView

ViewFlipper

注:这些类的子类是不支持的。

App Widget添加边距

作为一个widget,它不应该扩展到屏幕的边缘,同时在视觉上,不应该与其它widget相混淆。因此,你需要为你的widget在四个方向上添加边距。

Android4.0中,所有的widget都自动添加了内边距,并且提供了更好的对齐方案。要利用这种优势,建议设置应用的targetSdkVersion14或更高。

为了支持以前的设备,你也可以为早些的平台写一个包含边距的布局,而对于Android4.0以上的,则不设置:

        1.       设置targetSdkVersion14或更高

        2.       创建一个布局:

Xml代码  收藏代码

  1. <FrameLayout  

  2.   android:layout_width="match_parent"  

  3.   android:layout_height="match_parent"  

  4.   android:layout_margin="@dimen/widget_margin">  

  5.   

  6.   <LinearLayout  

  7.     android:layout_width="match_parent"  

  8.     android:layout_height="match_parent"  

  9.     android:orientation="horizontal"  

  10.     android:background="@drawable/my_widget_background">  

  11.     …  

  12.   </LinearLayout>  

  13.   

  14. </FrameLayout>  

  1.        创建两个dimension资源,res/values为老的平台,res/values-v144.0平台:

res/values/dimens.xml

Xml代码  收藏代码

  1. <dimen name="widget_margin">15dp</dimen>  

res/values-v14/dimens.xml

Xml代码  收藏代码

  1. <dimen name="widget_margin">0dp</dimen>  

还有一种做法是在9-patch的背景图里设置边距,并且为不同的版本提供不同的背景图。

使用AppWidgetProvider

AppWidgetProvider类继承了BroadcastReceiver,它只监听与App Widget有关的广播事件,如更新、删除、启用、禁用。当这些事件发生时,AppWidgetProvider将接收到以下事件:

        l onUpdate()

updatePeriodMills定义的时间间隔触发。当然,添加Widget的时候也会,因此,应该在此处执行一些必要的配置,如定义View的事件处理handler,有必要的话,还会启动一个临时的Service。然而,如果你声明了一个配置Activity,该方法将不会在此时被调用。

  l onDelete(Context, int[])

    当App Widgethost中移除时会被调用。

       l  onEnabled(Context)

    当App Widget的实例被第一次创建时,该方法将被调用。如果你建了两个实例,那该方法也只会被调用一次。

       l  onDisabled(Context)

    当最后一个App Widget的实例被删除时,该方法被调用。你可以在此处清除之前在onEnabled里执行的操作。

         l onReceive(Content, Intent)

      每次接收到广播都会被调用,而且执行的顺序在上述方法之前。通常,你不需要实现该方法,因为AppWidgetProvider已经对各种不同类型的广播进行了过滤及分发。

注:在Android1.5中,有一个已知的问题,使得在某些情况下,onDeleted不会被调用。这时,你就需要实现onReceive()了。

AppWidgetProvider的最重要的回调方法是onUpdate。如果你的App Widget不需要创建任何临时文件或数据库,或执行任何的清理工作,这应该是你唯一需要定义的。比如说,如果你的App Widget只包含一个按钮,它的功能仅仅是当按钮点击时打开一个Activity,你可以使用以下的实现:

Java代码  收藏代码

  1. public class ExampleAppWidgetProvider extends AppWidgetProvider {  

  2.   

  3.     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {  

  4.         final int N = appWidgetIds.length;  

  5.   

  6.         // Perform this loop procedure for each App Widget that belongs to this provider  

  7.         for (int i=0; i<N; i++) {  

  8.             int appWidgetId = appWidgetIds[i];  

  9.   

  10.             // Create an Intent to launch ExampleActivity  

  11.             Intent intent = new Intent(context, ExampleActivity.class);  

  12.             PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);  

  13.   

  14.             // Get the layout for the App Widget and attach an on-click listener  

  15.             // to the button  

  16.             RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);  

  17.             views.setOnClickPendingIntent(R.id.button, pendingIntent);  

  18.   

  19.             // Tell the AppWidgetManager to perform an update on the current app widget  

  20.             appWidgetManager.updateAppWidget(appWidgetId, views);  

  21.         }  

  22.     }  

  23. }  

       这个AppWidgetProvider只定义了一个onUpdate方法。它使用setOnClickPendingIntent方法设置了按钮点击时要执行的PendingIntent。该PendingIntent的作用就是启动ExampleActivity。注意,此处,我们枚举了appWidgetIds里所有的id,这些id是用来标识该AppWidgetProvider定义的所有App Widget。这样,如果用户创建了多个App Widget的实例,就会在同时被更新。对于同类的App Widget,系统只会维护一个updatePeriodMillis,这样,只有第一个实例创建时的更新周期才会起作用。例如,周期为2小时,第一个widget实例创建后1小时创建第二个实例,这样,在第二个实例创建后一小时就会被更新。

注意,由于AppWidgetProvider继承自BroadcastReceiver,如果你的操作需要耗时较长,最好在onUpdate里启动一个Service

接收App Widget广播Intent

AppWidgetProvider只是一个方便你编程的类。如果你想直接接收App Widget,你也可以实现你自己的BroadcastReceiver或者复写onReceive方法。你需要关注的是以下四种Intent

        l ACTION_APPWIDGET_UPDATE

        l ACTION_APPWIDGET_DELETED

        l ACTION_APPWIDGET_ENABLED

        l  ACTION_APPWIDGET_DISABLED

创建一个App Widget的配置Activity

如果你想让用户在添加新的App Widget时能够进行配置,你可以创建一个配置Activity。当App Widget创建时,这个Activity会被App Widget host调用,你可以在该Activity中允许用户设置一些诸如颜色、大小、更新周期等信息。

配置Activity与别的Activity没什么不同,唯一需要做的就是监听ACTION_APPWIDGET_CONFIGURE的事件。

Xml代码  收藏代码

  1. <activity android:name=".ExampleAppWidgetConfigure">  

  2.     <intent-filter>  

  3.         <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>  

  4.     </intent-filter>  

  5. </activity>  

 同时,这个Activity还需要在AppWidgetProviderInfoxml文件里声明。

Xml代码  收藏代码

  1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  

  2.     ...  

  3.     android:configure="com.example.android.ExampleAppWidgetConfigure"   

  4.     ... >  

  5. </appwidget-provider>  

     注意:由于这个Activity会在你的包外部被引用,此处必须使用全路径。

有了以上的说明,你就可以开始实现一个配置Activity了。当然,还有两件事你需要记住:

由于是被动调用的,你的配置Activity必须返回一个结果,并且带上App WidgetID(在传来的Intent里有,int类型的AppWidgetManager.EXTRA_APPWIDGET_ID)。

当配置Activity存在时,onUpdate方法不会被调用。这样,在配置Activity中就需要请求AppWidgetManager执行一次更新。

从配置Activity更新App Widget

       1、  获取App WidgetID

Java代码  收藏代码

  1. Intent intent = getIntent();  

  2. Bundle extras = intent.getExtras();  

  3. if (extras != null) {  

  4.     mAppWidgetId = extras.getInt(  

  5.             AppWidgetManager.EXTRA_APPWIDGET_ID,   

  6.             AppWidgetManager.INVALID_APPWIDGET_ID);  

  7. }  

        2、  执行配置过程

        3、  当配置完成后,获取AppWidgetManager的实例

Java代码  收藏代码

  1. AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);  

         4、  更新App Widget

Java代码  收藏代码

  1. RemoteViews views = new RemoteViews(context.getPackageName(),  

  2. R.layout.example_appwidget);  

  3. appWidgetManager.updateAppWidget(mAppWidgetId, views);  

         5、  创建返回的Intent,设其为结果,并关闭Activity

Java代码  收藏代码

  1. Intent resultValue = new Intent();  

  2. resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);  

  3. setResult(RESULT_OK, resultValue);  

  4. finish();  

 注:当你的配置Activity第一次打开时,请将结果设为RESULT_CANCELED。这样,如果用户中途使用返回键退出,App Widget host就会接收到通知,知道配置过程未完成。这样App Widget就不会被添加。

设置预览图片

Android3.0开始,引入了一个previewImage字段。用户可以在widget picker中看到该预览图。如果没设置该字段,系统将使用应用的图标。

Xml代码  收藏代码

  1. <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  

  2.   ...  

  3.   android:previewImage="@drawable/preview">  

  4. </appwidget-provider>  

为了方便开发者创建预览图,Android模拟器中提供了一个Widget Preview应用,你可以利用该应用创建预览图并将保存后的图片放到你的应用中去。

App Widget中使用Collection

Android3.0开始,你可以在App Widget中使用collection。这种widget使用RemoteViewsService来显示远程数据(比如说来自content provider)。由RemoteViewsService提供的数据将使用以下view来显示:

        l ListView:以垂直可拉伸列表显示。

        l  GridView:以二维可拉伸网格显示。

        l StackView:栈式卡片视图,用户可以上下拖动以查看前一张或后一张。

        l  AdapterViewFlipper:一个基于adapter的简单ViewAnimator,它可以在两个或多个视图间切换,且同一时刻只有一个子项被显示。

就像上面所说的,这些collection视图基于远程数据显示。也就是说,它们使用一个Adapter来绑定界面和后台数据。在widget的上下文中,我们使用RemoteViewsFactory代替Adapter,它是Adapter接口的一个简单封装。当请求集合中的特定数据时,RemoteViewsFactoryRemoteViews对象的方式创建并返回集合中的内容。为了在你的app widget中使用collection视图,你必须实现RemoteViewsServiceRemoteViewsFactory

RemoteViewsService是一个允许远程adapter请求RemoteViews对象的服务。

RemoteViewsFactory是一个collection视图与相应数据之间的接口。具体例子可以参照StackWidget

实现带有collectionApp Widget

要实现一个带有collectionApp Widget,你需要执行以下基本步骤。

Manifest中声明

除了以上列出的内容,你还需要将collection绑定到RemoteViewsService上。你需要为RemoteViewsService声明BIND_REMOTEVIEWS权限。这样可以限制其他应用随便访问你的widget的数据。如果你要实现一个RemoteViewsService来创建collection视图,可以按照如下方式:

Xml代码  收藏代码

  1. <service android:name="MyWidgetService"  

  2. ...  

  3. android:permission="android.permission.BIND_REMOTEVIEWS" />  

 Collection的布局

布局中最主要的部分就是collection视图:ListViewGridViewStackViewAdapterViewFlipper。以下是一个例子:

Xml代码  收藏代码

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

  2.   

  3. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  

  4.     android:layout_width="match_parent"  

  5.     android:layout_height="match_parent">  

  6.     <StackView xmlns:android="http://schemas.android.com/apk/res/android"  

  7.         android:id="@+id/stack_view"  

  8.         android:layout_width="match_parent"  

  9.         android:layout_height="match_parent"  

  10.         android:gravity="center"  

  11.         android:loopViews="true" />  

  12.     <TextView xmlns:android="http://schemas.android.com/apk/res/android"  

  13.         android:id="@+id/empty_view"  

  14.         android:layout_width="match_parent"  

  15.         android:layout_height="match_parent"  

  16.         android:gravity="center"  

  17.         android:background="@drawable/widget_item_background"  

  18.         android:textColor="#ffffff"  

  19.         android:textStyle="bold"  

  20.         android:text="@string/empty_view_text"  

  21.         android:textSize="20sp" />  

  22. </FrameLayout>  

 注意:empty view是当没数据时显示的视图。

除了App Widget的整体视图,你还需要为collection中的每一项定义布局。比如说,StackView Widget的例子中,只有一种。而WeatherListWidget的例子却有两种布局文件。

带有collectionAppWidgetProvider

与普通App Widget的唯一区别就是,在带有collectionAppWidgetProvider.onUpdate中,你需要调用setRemoteAdapter方法。通过调用这个方法,collection就知道如何获取它的数据。然后你在RemoteViewsService中返回一个RemoteViewsFactory,这样widget就能获取到相应的数据了。另外,当你调用setRemoteAdapter时,你需要传入一个Intent。这个Intent指向了RemoteViewsService的实现,并指明了App WidgetID

Java代码  收藏代码

  1. public void onUpdate(Context context, AppWidgetManager appWidgetManager,  

  2. int[] appWidgetIds) {  

  3.     // update each of the app widgets with the remote adapter  

  4.     for (int i = 0; i < appWidgetIds.length; ++i) {  

  5.           

  6.         // Set up the intent that starts the StackViewService, which will  

  7.         // provide the views for this collection.  

  8.         Intent intent = new Intent(context, StackWidgetService.class);  

  9.         // Add the app widget ID to the intent extras.  

  10.         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);  

  11.         intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));  

  12.         // Instantiate the RemoteViews object for the App Widget layout.  

  13.         RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);  

  14.         // Set up the RemoteViews object to use a RemoteViews adapter.   

  15.         // This adapter connects  

  16.         // to a RemoteViewsService  through the specified intent.  

  17.         // This is how you populate the data.  

  18.         rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent);  

  19.           

  20.         // The empty view is displayed when the collection has no items.   

  21.         // It should be in the same layout used to instantiate the RemoteViews  

  22.         // object above.  

  23.         rv.setEmptyView(R.id.stack_view, R.id.empty_view);  

  24.   

  25.         //  

  26.         // Do additional processing specific to this app widget...  

  27.         //  

  28.           

  29.         appWidgetManager.updateAppWidget(appWidgetIds[i], rv);     

  30.     }  

  31.     super.onUpdate(context, appWidgetManager, appWidgetIds);  

  32. }  

 RemoteViewsService

 

就像上面所说的,你的RemoteViewsService指向了用于创建远程collection视图的RemoteViewsFactory

你需要执行以下操作:

        1、 继承RemoteViewsService

        2、  在你的RemoteViewsService子类里,实现一个RemoteViewsFactory的内部类。

注意:你不能依赖一个Service的实力来保存数据。除非是静态数据,否则任何内容都不应该保存在此处。如果你要保存你的App Widget的数据,你可以使用ContentProvider

RemoteViewsFactory接口

你的实现了RemoteViewsFactory的类为App Widget提供了collection中的各项所需的数据。

其中,最重要的两个需要实现的方法是onCreategetViewAt

当系统第一次创建factory对象时,会调用onCreate。在这里面,你可以配置数据源。通过创建内容集合或Cursor等等。例如,在StackView Widget中,onCreate里创建了一个WidgetItem的数组。当你的App Widget处于激活状态时,系统会使用它们的index来获取数据并显示。

Java代码  收藏代码

  1. class StackRemoteViewsFactory implements  

  2. RemoteViewsService.RemoteViewsFactory {  

  3.     private static final int mCount = 10;  

  4.     private List<WidgetItem> mWidgetItems = new ArrayList<WidgetItem>();  

  5.     private Context mContext;  

  6.     private int mAppWidgetId;  

  7.   

  8.     public StackRemoteViewsFactory(Context context, Intent intent) {  

  9.         mContext = context;  

  10.         mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,  

  11.                 AppWidgetManager.INVALID_APPWIDGET_ID);  

  12.     }  

  13.   

  14.     public void onCreate() {  

  15.         // In onCreate() you setup any connections / cursors to your data source. Heavy lifting,  

  16.         // for example downloading or creating content etc, should be deferred to onDataSetChanged()  

  17.         // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR.  

  18.         for (int i = 0; i < mCount; i++) {  

  19.             mWidgetItems.add(new WidgetItem(i + "!"));  

  20.         }  

  21.         ...  

  22.     }  

  23. ...  

 getViewAt用于返回特定位置上的RemoteViews对象。

Java代码  收藏代码

  1. public RemoteViews getViewAt(int position) {  

  2.      

  3.     // Construct a remote views item based on the app widget item XML file,   

  4.     // and set the text based on the position.  

  5.     RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);  

  6.     rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);  

  7.   

  8.     ...  

  9.     // Return the remote views object.  

  10.     return rv;  

  11. }  

 为单独的item设置相应的行为

 

前文说过,通常来说,你是使用setOnClickPendingIntent方法来设置一个控件的点击事件。但对于collection中的子项,该方法是无效的。你可以先用setPendingIntentTemplate方法为collection整体的点击设置一个处理的PendingIntent,然后通过RemoteViewsFactory使用setOnClickFillInIntentcollection视图中的每一项传入一个与该项相关的Intent。该Intent会被合入处理时接收到Intent中。

onUpdate中设置pending intent template

 

Java代码  收藏代码

  1. // This section makes it possible for items to have individualized behavior.  

  2. // It does this by setting up a pending intent template. Individuals items of a collection  

  3. // cannot set up their own pending intents. Instead, the collection as a whole sets  

  4. // up a pending intent template, and the individual items set a fillInIntent  

  5. // to create unique behavior on an item-by-item basis.  

  6. Intent toastIntent = new Intent(context, StackWidgetProvider.class);  

  7. // Set the action for the intent.  

  8. // When the user touches a particular view, it will have the effect of  

  9. // broadcasting TOAST_ACTION.  

  10. toastIntent.setAction(StackWidgetProvider.TOAST_ACTION);  

  11. toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);  

  12. intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));  

  13. PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent,  

  14.     PendingIntent.FLAG_UPDATE_CURRENT);  

  15. rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent);  

 

RemoteViewsFactory中设置fill-in intent

Java代码  收藏代码

  1. public RemoteViews getViewAt(int position) {  

  2.     // position will always range from 0 to getCount() - 1.  

  3.   

  4.     // Construct a RemoteViews item based on the app widget item XML file, and set the  

  5.     // text based on the position.  

  6.     RemoteViews rv = new RemoteViews(mContext.getPackageName(), R.layout.widget_item);  

  7.     rv.setTextViewText(R.id.widget_item, mWidgetItems.get(position).text);  

  8.   

  9.     // Next, set a fill-intent, which will be used to fill in the pending intent template  

  10.     // that is set on the collection view in StackWidgetProvider.  

  11.     Bundle extras = new Bundle();  

  12.     extras.putInt(StackWidgetProvider.EXTRA_ITEM, position);  

  13.     Intent fillInIntent = new Intent();  

  14.     fillInIntent.putExtras(extras);  

  15.     // Make it possible to distinguish the individual on-click  

  16.     // action of a given item  

  17.     rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);   

  18.     ...   

  19.     // Return the RemoteViews object.  

  20.     return rv;  

  21. }  

 保证Collection的数据是最新的

 

下图给出了当更新发生时,一个带有collection视图的App Widget的工作流。


App Widget详解
 

带有collection视图的App Widget的一个作用就是为用户提供实时的数据。例如,Android 3.0上的Gmail app widget,它为洪湖提供了他们的收件箱的快照。要实现这个功能,你需要能够触发你的RemoteViewsFactorycollection视图去获取以及显示新的数据。你可以通过调用AppWidgetManagernotifyAppWidgetViewDataChanged方法来达到这个目的。

通过调用这个方法,RemoteViewsFactoryonDataSetChanged方法将被调用,你可以在其中进行数据的获取。

onDataSetChanged以及后续的getViewAt方法中,你都可以进行一些处理密集型的操作。也就是说,你不用怕这个操作会占用太长的时间,从而导致UI线程无响应。

如果getViewAt方法耗时太长,加载视图(可由RemoteViewsFactorygetLoadingView获取)将显示在collection视图所在的区域。


你可能感兴趣的:(App Widget详解)