Android App Widgets

来源:互联网 发布:淘宝模特小倩 编辑:程序博客网 时间:2024/06/01 08:47

App Widgets

Quickview

  • App Widgets provide users access to some of your application featuresdirectly from the Home screen (without the need to launch an activity)
  • App Widgets are backed by a special kind of broadcast receiver thathandles the AppWidget lifecycle

In this document

  1. The Basics
  2. Declaring an App Widget in the Manifest
  3. Adding the AppWidgetProviderInfo Metadata
  4. Creating the App Widget Layout
  5. Using the AppWidgetProvider Class
    1. Receiving App Widget broadcastIntents
  6. Creating an App Widget ConfigurationActivity
    1. Updating the App Widgetfrom the configuration Activity
  7. Setting a Preview Image
  8. Using App Widgets with Collections
    1. Sample application
    2. Implementing app widgets withcollections
    3. Keeping Collection Data Fresh

Key classes

  1. AppWidgetProvider
  2. AppWidgetProviderInfo
  3. AppWidgetManager

See also

  1. App WidgetDesign Guidelines
  2. Introducing home screen widgets and the AppWidget framework »

App Widgets are miniature application views that can be embedded in otherapplications(such as the Home screen) and receive periodic updates. These views arereferred to as Widgets in the user interface,and you can publish one with an App Widget provider. An application componentthat is able to hold other App Widgets is called an App Widget host. The screenshotbelow showsthe Music App Widget.


This document describes how to publish an App Widget using an App Widgetprovider.

The Basics

To create an App Widget, you need the following:

AppWidgetProviderInfo object
Describes the metadata for an App Widget, such as the App Widget's layout,update frequency, and the AppWidgetProvider class. This should be defined in XML.
AppWidgetProvider class implementation
Defines the basic methods that allow you to programmatically interfacewith the App Widget, based on broadcast events. Through it, you will receive broadcasts when theApp Widget is updated, enabled, disabled and deleted.
View layout
Defines the initial layout for the App Widget, defined in XML.

Additionally, you can implement an App Widget configuration Activity. This isan optionalActivity that launches when the user adds your App Widgetand allows him or herto modify App Widget settings at create-time.

The following sections describe how to setup each of these components.

Declaring an App Widget in the Manifest

First, declare the AppWidgetProvider class in yourapplication'sAndroidManifest.xml file. For example:

<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>

The <receiver> element requires theandroid:name attribute, which specifies theAppWidgetProvider usedby the App Widget.

The <intent-filter> element must include an<action>element with theandroid:name attribute. This attribute specifiesthat theAppWidgetProvider accepts theACTION_APPWIDGET_UPDATE broadcast.This is the only broadcast that you must explicitly declare. TheAppWidgetManagerautomatically sends all other App Widget broadcasts to the AppWidgetProvider asnecessary.

The <meta-data> element specifies theAppWidgetProviderInfo resource and requires the following attributes:

  • android:name - Specifies the metadata name. Useandroid.appwidget.provider to identify the data as theAppWidgetProviderInfodescriptor.
  • android:resource - Specifies the AppWidgetProviderInfo resource location.

Adding the AppWidgetProviderInfo Metadata

The AppWidgetProviderInfo defines the essential qualities of an App Widget, such as its minimum layout dimensions, its initiallayout resource,how often to update the App Widget, and (optionally) a configuration Activity tolaunch at create-time.Define the AppWidgetProviderInfo object in an XML resource using a single<appwidget-provider> element and save it in the project'sres/xml/ folder.

For example:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"    android:minWidth="294dp"    android:minHeight="72dp"    android:updatePeriodMillis="86400000"    android:previewImage="@drawable/preview"    android:initialLayout="@layout/example_appwidget"    android:configure="com.example.android.ExampleAppWidgetConfigure"     android:resizeMode="horizontal|vertical"></appwidget-provider>

Here's a summary of the <appwidget-provider> attributes:

  • The values for the minWidth and minHeightattributes specify the minimum area required by the App Widget's layout.

    The default Home screen positions App Widgets in its window based on agrid of cells that have a defined height and width. If the values for an AppWidget's minimum width or height don't match the dimensions of the cells, then the App Widget dimensions roundup to the nearest cell size. (See the App WidgetDesign Guidelines for more information on the Home screen cell sizes.)

    Because the Home screen's layout orientation (and thus, the cell sizes)can change, as a rule of thumb, you should assume the worst-case cell size of 74 pixelsfor the heightand width of a cell. However, you must subtract 2 from the finaldimension to account for any integer rounding errors that occur in the pixel count. To find yourminimum width and height in density-independent pixels (dp), use this formula:
    (number of cells * 74) - 2
    Following this formula, you should use 72 dp for a height of one cell, 294dp and for a width of four cells.

    Note: To make your app widget portable acrossdevices, your app widget's minimum size should never be larger than 4 x 4 cells.See theAppWidget Design Guidelines for more discussion of Home screen cell sizes.

  • The updatePeriodMillis attribute defines how often the AppWidget framework should request an update from theAppWidgetProvider by calling theonUpdate() callback method. The actual updateis not guaranteed to occur exactly on time with this value and we suggestupdating as infrequently as possible—perhaps no more than once an hour toconserve the battery. You might also allow the user to adjust the frequency in aconfiguration—some people might want a stock ticker to update every 15minutes, or maybe only four times a day.

    Note: If the device is asleep when itis time for an update (as defined byupdatePeriodMillis), then the device willwake up in order to perform the update. If you don't update more than once per hour, thisprobably won't cause significant problems for the battery life. If, however, you needto update more frequently and/or you do not need to update while the device is asleep,then you can instead perform updates based on an alarm that will not wake the device. To doso, set an alarm with an Intent that your AppWidgetProvider receives, using theAlarmManager. Set the alarm type to eitherELAPSED_REALTIME orRTC, which will only deliver the alarm when the device is awake. Then setupdatePeriodMillis to zero ("0").

  • The initialLayout attribute points to the layout resourcethat defines the App Widget layout.
  • The configure attribute defines the Activity to launch when the user adds the App Widget, in order for him or her to configure AppWidget properties. This is optional (read Creating an App Widget ConfigurationActivity below).
  • The previewImage attribute specifies a preview of what theapp widget will look like after it's configured, which the user sees whenselecting the app widget. If not supplied, the user instead sees yourapplication's launcher icon. This field corresponds to theandroid:previewImage attribute in the <receiver>element in theAndroidManifest.xml file. For more discussion ofusingpreviewImage, seeSetting a PreviewImage. Introduced in Android 3.0.
  • The autoAdvanceViewId attribute specifies the view ID of theapp widget subview that should be auto-advanced by the widget's host. Introduced in Android 3.0.
  • The resizeMode attribute specifies the rules by which a widgetcan be resized. You use this attribute to make homescreen widgetsresizeable—horizontally, vertically, or on both axes. Users touch-hold awidget to show its resize handles, then drag the horizontal and/or verticalhandles to change the size on the layout grid. Values for theresizeMode attribute include "horizontal", "vertical", and "none".To declare a widget as resizeable horizontally and vertically, supply the value"horizontal|vertical". Introduced in Android 3.1.

See the AppWidgetProviderInfo class for moreinformation on theattributes accepted by the<appwidget-provider> element.

Creating the App Widget Layout

You must define an initial layout for your App Widget in XML and save it inthe project'sres/layout/ directory. You can design your App Widget using theView objects listedbelow, but before you begin designing your App Widget, please read andunderstand theApp WidgetDesign Guidelines.

Creating the App Widget layout is simple if you'refamiliar with Declaring Layout inXML.However, you must be aware that App Widget layouts are based onRemoteViews,which do not support every kind of layout or view widget.

A RemoteViews object (and, consequently, an App Widget) can support the following layout classes:

  • FrameLayout
  • LinearLayout
  • RelativeLayout

And the following widget classes:

  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper

Descendants of these classes are not supported.

Using the AppWidgetProvider Class

The AppWidgetProvider class extendsBroadcastReceiver as a convenienceclass to handle the App Widget broadcasts. The AppWidgetProvider receives onlythe event broadcasts thatare relevant to the App Widget, such as when the App Widget is updated, deleted,enabled, and disabled.When these broadcast events occur, the AppWidgetProvider receives the followingmethod calls:

onUpdate()
This is called to update the App Widget at intervals defined by theupdatePeriodMillis attribute in the AppWidgetProviderInfo (seeAdding the AppWidgetProviderInfo Metadata above). This method is also called when the user adds the App Widget, so it should perform the essential setup, such as define event handlers for Views and start a temporaryService, if necessary. However, if you have declared aconfiguration Activity,this method is not called when the user adds theApp Widget, but is called for the subsequent updates. It is the responsibility of the configuration Activity to perform the first update when configuration isdone. (SeeCreating an App Widget ConfigurationActivity below.)
onDeleted(Context, int[])
This is called every time an App Widget is deleted from the App Widgethost.
onEnabled(Context)
This is called when an instance the App Widget is created for the firsttime. For example, if the user adds two instances of your App Widget, this is only called the first time. If you need to open a new database or perform other setup that only needs tooccur once for all App Widget instances, then this is a good place to do it.
onDisabled(Context)
This is called when the last instance of your App Widget is deleted fromthe App Widget host. This is where you should clean up any work done inonEnabled(Context), such as delete a temporary database.
onReceive(Context, Intent)
This is called for every broadcast and before each of the above callbackmethods. You normally don't need to implement this method because the defaultAppWidgetProvider implementation filters all App Widget broadcasts and calls the above methods as appropriate.

Note: In Android 1.5, there is a known issuein which theonDeleted() method will not be called when it should be. To workaround this issue, you can implementonReceive() as described in thisGroup postto receive theonDeleted() callback.

The most important AppWidgetProvider callback is onUpdate() because it is called wheneach App Widget is added to a host (unless you use a configuration Activity). Ifyour App Widget accepts any user interaction events, then you need to registerthe event handlers in this callback. If your App Widget doesn't create temporaryfiles or databases, or perform other work that requires clean-up, then onUpdate() may be the only callbackmethod you need to define. For example, if you want an App Widget with a buttonthat launches an Activity when clicked, you could use the followingimplementation of 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);        }    }}

This AppWidgetProvider defines only the onUpdate() method for the purpose ofdefining a PendingIntent that launches anActivity and attaching it to the App Widget's button withsetOnClickPendingIntent(int, PendingIntent). Noticethat it includes a loop that iterates through each entry inappWidgetIds, which is an array of IDs that identify each AppWidget created by this provider. In this way, if the user creates more than oneinstance of the App Widget, then they are all updated simultaneously. However,only oneupdatePeriodMillis schedule will be managed for allinstances of the App Widget. For example, if the update schedule is defined tobe every two hours, and a second instance of the App Widget is added one hourafter the first one, then they will both be updated on the period defined by thefirst one and the second update period will be ignored (they'll both be updatedevery two hours, not every hour).

Note: Because AppWidgetProvider is an extension ofBroadcastReceiver, your process is not guaranteed to keeprunning after the callback methods return (seeBroadcastReceiver for information about the broadcastlifecycle). If your App Widget setup process can take several seconds (perhapswhile performing web requests) and you require that your process continues,consider starting aService in theonUpdate() method. From within the Service, you can perform your own updatesto the App Widget without worrying about the AppWidgetProvider closing down dueto anApplicationNot Responding (ANR) error. See the Wiktionary sample'sAppWidgetProvider for an example of an App Widget running aService.

Also see the ExampleAppWidgetProvider.java sample class.

Receiving App Widget broadcast Intents

AppWidgetProvider is just a convenience class. Ifyou would liketo receive the App Widget broadcasts directly, you can implement your ownBroadcastReceiver or override theonReceive(Context, Intent) callback. The four Intents you need to care about are:

  • ACTION_APPWIDGET_UPDATE
  • ACTION_APPWIDGET_DELETED
  • ACTION_APPWIDGET_ENABLED
  • ACTION_APPWIDGET_DISABLED

Creating an App Widget Configuration Activity

If you would like the user to configure settings when he or she adds a newApp Widget,you can create an App Widget configuration Activity. ThisActivity will be automatically launched by the App Widget host and allows the user toconfigureavailable settings for the App Widget at create-time, such as the App Widgetcolor, size, update period or other functionality settings.

The configuration Activity should be declared as a normal Activity in theAndroid manifest file.However, it will be launched by the App Widget host with theACTION_APPWIDGET_CONFIGURE action,so the Activity needs to accept this Intent. For example:

<activity android:name=".ExampleAppWidgetConfigure">    <intent-filter>        <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>    </intent-filter></activity>

Also, the Activity must be declared in the AppWidgetProviderInfo XML file,with theandroid:configure attribute (seeAdding the AppWidgetProviderInfo Metadata above). For example, the configurationActivitycan be declared like this:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"    ...    android:configure="com.example.android.ExampleAppWidgetConfigure"     ... ></appwidget-provider>

Notice that the Activity is declared with a fully-qualified namespace,because it will be referenced from outside your package scope.

That's all you need to get started with a configuration Activity. Now all youneed is the actualActivity. There are, however, two important things to remember when youimplement the Activity:

  • The App Widget host calls the configuration Activity and the configurationActivity should always return a result. The result should include the App Widget ID passed by the Intent that launched the Activity (saved in the Intent extrasasEXTRA_APPWIDGET_ID).
  • The onUpdate() methodwill not be called when the App Widgetis created (the system will not send the ACTION_APPWIDGET_UPDATE broadcast when aconfiguration Activity is launched). It is the responsibility of the configuration Activity torequest an update from the AppWidgetManager when the App Widget is first created. However, onUpdate() will be called for subsequent updates—it is only skippedthe first time.

See the code snippets in the following section for an example of how toreturn a resultfrom the configuration and update the App Widget.

Updating the App Widget from theconfiguration Activity

When an App Widget uses a configuration Activity, it is the responsibility ofthe Activityto update the App Widget when configuration is complete. You can do so by requesting an update directly from theAppWidgetManager.

Here's a summary of the procedure to properly update the App Widget and closethe configuration Activity:

  1. First, get the App Widget ID from the Intent that launched the Activity:
    Intent intent = getIntent();Bundle extras = intent.getExtras();if (extras != null) {    mAppWidgetId = extras.getInt(            AppWidgetManager.EXTRA_APPWIDGET_ID,             AppWidgetManager.INVALID_APPWIDGET_ID);}
  2. Perform your App Widget configuration.
  3. When the configuration is complete, get an instance of theAppWidgetManager by callinggetInstance(Context):
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
  4. Update the App Widget with a RemoteViews layout bycallingupdateAppWidget(int, RemoteViews):
    RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.example_appwidget);appWidgetManager.updateAppWidget(mAppWidgetId, views);
  5. Finally, create the return Intent, set it with the Activity result, andfinish the Activity:
    Intent resultValue = new Intent();resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);setResult(RESULT_OK, resultValue);finish();

Tip: When your configuration Activity firstopens, setthe Activity result to RESULT_CANCELED. This way, if the user backs-out of theActivity beforereaching the end, the App Widget host is notified that the configuration wascancelled and theApp Widget will not be added.

See the ExampleAppWidgetConfigure.java sample class in ApiDemos for an example.

Setting a Preview Image

Android 3.0 introduces the previewImage field, which specifies apreview of what the app widget looks like. This preview is shown to the user from thewidget picker. If this field is not supplied, the app widget's icon is used forthe preview.

This is how you specify this setting in XML:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"  ...  android:previewImage="@drawable/preview"></appwidget-provider>

To help create a preview image for your app widget (to specify in the previewImage field), the Androidemulator includes an application called "Widget Preview." To create apreview image, launch this application, select the app widget for yourapplication and set it up how you'd like your preview image to appear, then saveit and place it in your application's drawable resources.

Using App Widgets with Collections

Android 3.0 introduces App Widgets with collections. These kinds of AppWidgets use theRemoteViewsService to display collectionsthat are backed by remote data, such as from acontentprovider. The data provided by theRemoteViewsServiceis presented in the App Widget using one of the following view types, whichwe’ll refer to as “collection views:”

ListView
A view that shows items in avertically scrollinglist. For an example, see the Gmail app widget.
GridView
A view that shows items intwo-dimensional scrolling grid. For an example, see the Bookmarks appwidget.
StackView
Astacked card view (kind of like a rolodex), where the user can flick the frontcard up/down to see the previous/next card, respectively. Examples includethe YouTube and Books app widgets. 
AdapterViewFlipper
An adapter-backed simpleViewAnimator that animates between two or more views. Only onechild is shown at a time.

As stated above, these collection views display collections backed by remotedata. This means that they use anAdapter to bind theiruser interface to their data. AnAdapter binds individualitems from a set of data into individualView objects.Because these collection views are backed by adapters, the Android frameworkmust include extra architecture to support their use in app widgets. In thecontext of an app widget, theAdapter is replaced by aRemoteViewsFactory,which is simply a thin wrapper around the Adapterinterface. Whenrequested for a specific item in the collection, theRemoteViewsFactory createsand returns the item for the collection as aRemoteViewsobject.In order to include a collection view in your app widget, youmust implementRemoteViewsService andRemoteViewsFactory.

RemoteViewsService is a service that allows a remoteadapter to requestRemoteViews objects.RemoteViewsFactory is aninterface for an adapter between a collection view (such asListView,GridView, and so on) and theunderlying data for that view. From theStackView Widgetsample, here is an example of the boilerplate code you use to implement this service and interface:

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.}

Sample application

The code excerpts in this section are drawn from the StackView Widgetsample:


This sample consists of a stack of 10 views, which display the values"0!" through"9!" The sampleapp widget has these primary behaviors:

  • The user can vertically fling the top view in theapp widget to display the next or previous view. This is a built-in StackViewbehavior.
  • Without any user interaction, the app widget automatically advancesthroughits views in sequence, like a slide show. This is due to the settingandroid:autoAdvanceViewId="@id/stack_view" in theres/xml/stackwidgetinfo.xml file. This setting applies to the viewID,which in this case is the view ID of the stack view.
  • If the user touches the top view, the app widget displays the Toast message "Touched viewn," wheren is the index (position) of the touched view. For more discussion ofhow this is implemented, seeAdding behavior to individual items.

Implementing app widgets with collections

To implement an App Widget with collections, you follow the same basic steps you would use to implement any app widget. The following sections describe theadditional steps you need to perform to implement an App Widget withcollections.

Manifest for app widgets with collections

In addition to the requirements listed in Declaring anApp Widget in the Manifest, to make it possible for App Widgets withcollections to bind to yourRemoteViewsService, you mustdeclare the service in your manifest file with the permissionBIND_REMOTEVIEWS. This prevents other applicationsfrom freely accessing your app widget's data. For example, when creating an AppWidget that uses RemoteViewsService to populate acollection view, the manifest entry may look like this:

<service android:name="MyWidgetService"...android:permission="android.permission.BIND_REMOTEVIEWS" />

The line android:name="MyWidgetService"refers to your subclass ofRemoteViewsService.

Layout for app widgets with collections

The main requirement for your app widget layout XML file is that itinclude one of the collection views:ListView,GridView,StackView, orAdapterViewFlipper. Here is thewidget_layout.xml fortheStackViewWidget sample:

<?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>

Note that empty views must be siblings of the collection view for which theempty view represents empty state.

In addition to the layout file for your entire app widget, you must createanother layout file that defines the layout for each item in the collection (forexample, a layout for each book in a collection of books). For example, theStackView Widgetsample only has one layout file,widget_item.xml, since allitems use the same layout. But theWeatherListWidget sample has two layout files:dark_widget_item.xml andlight_widget_item.xml.

AppWidgetProvider class for app widgets with collections

As with a regular app widget, the bulk of your code in your AppWidgetProvider subclass typically goes inonUpdate(). The major difference inyour implementation foronUpdate() when creating an appwidget with collections is that you must call setRemoteAdapter(). This tells thecollection view where to get its data. TheRemoteViewsService can then return your implementation ofRemoteViewsFactory, andthe widget can serve up the appropriate data. When you call this method, youmust pass an intent that points to your implementation ofRemoteViewsService and the App Widget ID that specifies the appwidget to update.

For example, here's how the StackView Widget sample implements the onUpdate() callback method to setthe RemoteViewsService as the remote adapter for the app widgetcollection:

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 class

As described above, your RemoteViewsService subclassprovides theRemoteViewsFactory used to populate the remote collection view.

Specifically, you need toperform these steps:

  1. Subclass RemoteViewsService.RemoteViewsService is the service through whicha remote adapter can requestRemoteViews.
  2. In your RemoteViewsService subclass, include aclass that implements theRemoteViewsFactoryinterface.RemoteViewsFactory is an interface for an adapter between a remote collectionview (such asListView,GridView,and so on) and the underlying data for that view. Your implementation isresponsible for making aRemoteViews object for eachitem in the data set. This interface is a thin wrapper aroundAdapter.

The primary contents of the RemoteViewsServiceimplementation is itsRemoteViewsFactory,described below.

RemoteViewsFactory interface

Your custom class that implements the RemoteViewsFactoryinterface provides the app widget with the data for the items in its collection.Todo this, it combines your app widget item XML layout file with a source of data.This source of data could be anything from a database to a simple array. In theStackView Widgetsample, the data source is an array of WidgetItems. The RemoteViewsFactoryfunctions as an adapter to glue the data to the remote collection view.

The two most important methods you need to implement for yourRemoteViewsFactorysubclass areonCreate() andgetViewAt().

The system calls onCreate() whencreating your factory for the first time. This is where you set up anyconnections and/or cursors to your data source. For example, the StackView Widgetsample uses onCreate() toinitialize an array ofWidgetItem objects. When your app widget isactive, the system accesses these objects using their index position in thearray and the text they contain is displayed

Here is an excerpt from the the StackView Widgetsample's RemoteViewsFactory implementation that shows portions of theonCreate()method:

class StackRemoteViewsFactory implementsRemoteViewsService.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 + "!"));        }        ...    }...

The RemoteViewsFactory methodgetViewAt()returns aRemoteViews object corresponding to the data atthe specifiedposition in the data set. Here is an excerpt from theStackView Widget sample'sRemoteViewsFactoryimplementation:

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;}

Adding behavior to individual items

The above sections show you how to bind your data to your app widgetcollection. But what if you want to add dynamic behavior to the individual itemsin your collection view?

As described in Using the AppWidgetProviderClass, you normally use setOnClickPendingIntent() to set an object's clickbehavior—such as to cause a button to launch an Activity. But this approach is not allowed for child views in anindividual collection item (to clarify, you could use setOnClickPendingIntent() to set up a global buttonin the Gmail app widget that launches the app, for example, but not on theindividual list items). Instead, to add click behavior to individual items in acollection, you usesetOnClickFillInIntent(). This entails setting up up a pending intent templatefor your collection view, and then setting a fill-in intent on each item in thecollection via yourRemoteViewsFactory.

This section uses the StackView Widgetsample to describe how to add behavior to individual items. In theStackView Widgetsample, if the user touches the top view, the app widget displays theToast message "Touched viewn," wheren is the index (position) of the touched view. This is how itworks:

  • The StackWidgetProvider (an AppWidgetProvider subclass) creates a pending intent that hasa custom action calledTOAST_ACTION.
  • When the user touches a view, the intent is fired and it broadcastsTOAST_ACTION.
  • This broadcast is intercepted by the StackWidgetProvider'sonReceive() method, and the app widget displays theToast message for the touched view. The data for the collectionitems is provided by theRemoteViewsFactory, viatheRemoteViewsService.

Note: The StackView Widgetsample uses a broadcast, but typically an app widget would simply launch anactivity in a scenario like this one.

Setting up the pending intent template

The StackWidgetProvider (AppWidgetProvider subclass) sets up a pending intent.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 theindividual items set a fill-in intent to create unique behavior on anitem-by-itembasis.

This class also receives the broadcast that is sent when the user touches aview. It processes this event in itsonReceive() method. If the intent's action isTOAST_ACTION, the app widget displays aToastmessage for the current view.

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);    }}
Setting the fill-in Intent

Your RemoteViewsFactory must set a fill-in intent on each item in the collection.This makes it possible to distinguish the individual on-click action of a givenitem. The fill-in intent is then combined with the PendingIntent template in order to determine the final intent thatwill be executed when the item is clicked.

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;        }    ...    }

Keeping Collection Data Fresh

The following figure illustrates the flow that occurs in an App Widget thatusescollections when updates occur. It shows how the App Widget code interacts withtheRemoteViewsFactory, and how you can trigger updates:


One feature of App Widgets that use collections is the ability to provideusers with up-to-date content. For example, consider the Android 3.0 Gmailapp widget, which provides users with a snapshot of their inbox. To make thispossible, you need to be able to trigger your RemoteViewsFactory andcollection view to fetch and display new data. You achieve this with theAppWidgetManager callnotifyAppWidgetViewDataChanged(). This call results in a callback to yourRemoteViewsFactory’sonDataSetChanged() method, which gives you the opportunity to fetch any newdata. Note that you can performprocessing-intensive operations synchronously within theonDataSetChanged() callback. You are guaranteed that this call will becompleted before the metadata or view data is fetched from the RemoteViewsFactory. Inaddition, you can perform processing-intensive operations within thegetViewAt()method. If this call takes a long time, the loading view (specified by theRemoteViewsFactory’sgetLoadingView() method)will be displayed in the corresponding position of the collection view until itreturns.