App Widgets
In this document
- The Basics
- Declaring an App Widget in the Manifest
- Adding the AppWidgetProviderInfo Metadata
- Creating the App Widget Layout
- Using the AppWidgetProvider Class
- Pinning App Widgets
- Creating an App Widget Configuration Activity
- Setting a Preview Image
- Using App Widgets with Collections
Key classes
App Widgets are miniature application views that can be embedded in other applications (such as the Home screen) and receive periodic updates. These views are referred to as Widgets in the user interface, and you can publish one with an App Widget provider. An application component that is able to hold other App Widgets is called an App Widget host. The screenshot below shows the Music App Widget.
![](https://developer.android.com/images/appwidgets/appwidget.png)
This document describes how to publish an App Widget using an App Widget provider. For a discussion of creating your own AppWidgetHost
to host app widgets, see App Widget Host.
Widget Design
For information about how to design your app widget, read the Widgets design guide.
The Basics
To create an App Widget, you need the following:
- 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.
- Defines the basic methods that allow you to programmatically interface with the App Widget, based on broadcast events. Through it, you will receive broadcasts when the App Widget is updated, enabled, disabled and deleted. View layout
- Defines the initial layout for the App Widget, defined in XML.
AppWidgetProviderInfo
object
AppWidgetProvider
class implementation
Additionally, you can implement an App Widget configuration Activity. This is an optional Activity
that launches when the user adds your App Widget and allows him or her to modify App Widget settings at create-time.
The following sections describe how to set up each of these components.
Declaring an App Widget in the Manifest
First, declare the AppWidgetProvider
class in your application's AndroidManifest.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 the android:name
attribute, which specifies the AppWidgetProvider
used by the App Widget.
The <intent-filter>
element must include an <action>
element with the android:name
attribute. This attribute specifies that the AppWidgetProvider
accepts the ACTION_APPWIDGET_UPDATE
broadcast. This is the only broadcast that you must explicitly declare. The AppWidgetManager
automatically sends all other App Widget broadcasts to the AppWidgetProvider as necessary.
The <meta-data>
element specifies the AppWidgetProviderInfo
resource and requires the following attributes:
android:name
- Specifies the metadata name. Useandroid.appwidget.provider
to identify the data as theAppWidgetProviderInfo
descriptor.android:resource
- Specifies theAppWidgetProviderInfo
resource location.
Adding the AppWidgetProviderInfo Metadata
The AppWidgetProviderInfo
defines the essential qualities of an App Widget, such as its minimum layout dimensions, its initial layout resource, how often to update the App Widget, and (optionally) a configuration Activity to launch at create-time. Define the AppWidgetProviderInfo object in an XML resource using a single <appwidget-provider>
element and save it in the project's res/xml/
folder.
For example:
<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>
Here's a summary of the <appwidget-provider>
attributes:
- The values for the
minWidth
andminHeight
attributes specify the minimum amount of space the App Widget consumes by default. The default Home screen positions App Widgets in its window based on a grid of cells that have a defined height and width. If the values for an App Widget's minimum width or height don't match the dimensions of the cells, then the App Widget dimensions round up to the nearest cell size.See the App Widget Design Guidelines for more information on sizing your App Widgets.
Note: To make your app widget portable across devices, your app widget's minimum size should never be larger than 4 x 4 cells.
- The
minResizeWidth
andminResizeHeight
attributes specify the App Widget's absolute minimum size. These values should specify the size below which the App Widget would be illegible or otherwise unusable. Using these attributes allows the user to resize the widget to a size that may be smaller than the default widget size defined by theminWidth
andminHeight
attributes. Introduced in Android 3.1.See the App Widget Design Guidelines for more information on sizing your App Widgets.
- The
updatePeriodMillis
attribute defines how often the App Widget framework should request an update from theAppWidgetProvider
by calling theonUpdate()
callback method. The actual update is not guaranteed to occur exactly on time with this value and we suggest updating as infrequently as possible—perhaps no more than once an hour to conserve the battery. You might also allow the user to adjust the frequency in a configuration—some people might want a stock ticker to update every 15 minutes, or maybe only four times a day.Note: If the device is asleep when it is time for an update (as defined by
updatePeriodMillis
), then the device will wake up in order to perform the update. If you don't update more than once per hour, this probably won't cause significant problems for the battery life. If, however, you need to 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 do so, 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 resource that defines the App Widget layout. - The
configure
attribute defines theActivity
to launch when the user adds the App Widget, in order for him or her to configure App Widget properties. This is optional (read Creating an App Widget Configuration Activity below). - The
previewImage
attribute specifies a preview of what the app widget will look like after it's configured, which the user sees when selecting the app widget. If not supplied, the user instead sees your application's launcher icon. This field corresponds to theandroid:previewImage
attribute in the<receiver>
element in theAndroidManifest.xml
file. For more discussion of usingpreviewImage
, see Setting a Preview Image. Introduced in Android 3.0. - The
autoAdvanceViewId
attribute specifies the view ID of the app 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 widget can be resized. You use this attribute to make homescreen widgets resizeable—horizontally, vertically, or on both axes. Users touch-hold a widget to show its resize handles, then drag the horizontal and/or vertical handles 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. - The
minResizeHeight
attribute specifies the minimum height (in dps) to which the widget can be resized. This field has no effect if it is greater thanminHeight
or if vertical resizing isn't enabled (seeresizeMode
). Introduced in Android 4.0. - The
minResizeWidth
attribute specifies the minimum width (in dps) to which the widget can be resized. This field has no effect if it is greater thanminWidth
or if horizontal resizing isn't enabled (seeresizeMode
). Introduced in Android 4.0. - The
widgetCategory
attribute declares whether your App Widget can be displayed on the home screen (home_screen
), the lock screen (keyguard
), or both. Only Android versions lower than 5.0 support lock-screen widgets. For Android 5.0 and higher, onlyhome_screen
is valid.
See the AppWidgetProviderInfo
class for more information on the attributes 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 in the project's res/layout/
directory. You can design your App Widget using the View objects listed below, but before you begin designing your App Widget, please read and understand the App Widget Design Guidelines.
Creating the App Widget layout is simple if you're familiar with Layouts. However, you must be aware that App Widget layouts are based on RemoteViews
, 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:
And the following widget classes:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
Descendants of these classes are not supported.
RemoteViews also supports ViewStub
, which is an invisible, zero-sized View you can use to lazily inflate layout resources at runtime.
Adding margins to App Widgets
Widgets should not generally extend to screen edges and should not visually be flush with other widgets, so you should add margins on all sides around your widget frame.
As of Android 4.0, app widgets are automatically given padding between the widget frame and the app widget's bounding box to provide better alignment with other widgets and icons on the user's home screen. To take advantage of this strongly recommended behavior, set your application's targetSdkVersion to 14 or greater.
It's easy to write a single layout that has custom margins applied for earlier versions of the platform, and has no extra margins for Android 4.0 and greater:
- Set your application's
targetSdkVersion
to 14 or greater. - Create a layout such as the one below, that references a dimension resource for its margins:
<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>
- Create two dimensions resources, one in
res/values/
to provide the pre-Android 4.0 custom margins, and one inres/values-v14/
to provide no extra padding for Android 4.0 widgets:res/values/dimens.xml:
<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml:
<dimen name="widget_margin">0dp</dimen>
Another option is to simply build extra margins into your nine-patch background assets by default, and provide different nine-patches with no margins for API level 14 or later.
Using the AppWidgetProvider Class
You must declare your AppWidgetProvider class implementation as a broadcast receiver using the <receiver>
element in the AndroidManifest (seeDeclaring an App Widget in the Manifest above).
The AppWidgetProvider
class extends BroadcastReceiver as a convenience class to handle the App Widget broadcasts. The AppWidgetProvider receives only the event broadcasts that are 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 following method calls:
-
This is called to update the App Widget at intervals defined by the
updatePeriodMillis
attribute in the AppWidgetProviderInfo (see Adding 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 a configuration Activity, this method is not called when the user adds the App Widget, but is called for the subsequent updates. It is the responsibility of the configuration Activity to perform the first update when configuration is done. (See Creating an App Widget Configuration Activity below.) -
This is called when the widget is first placed and any time the widget is resized. You can use this callback to show or hide content based on the widget's size ranges. You get the size ranges by calling
getAppWidgetOptions()
, which returns aBundle
that includes the following:
OPTION_APPWIDGET_MIN_WIDTH
—Contains the lower bound on the current width, in dp units, of a widget instance.OPTION_APPWIDGET_MIN_HEIGHT
—Contains the lower bound on the current height, in dp units, of a widget instance.OPTION_APPWIDGET_MAX_WIDTH
—Contains the upper bound on the current width, in dp units, of a widget instance.OPTION_APPWIDGET_MAX_HEIGHT
—Contains the upper bound on the current width, in dp units, of a widget instance.
- This is called every time an App Widget is deleted from the App Widget host.
- This is called when an instance the App Widget is created for the first time. 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 to occur once for all App Widget instances, then this is a good place to do it.
-
This is called when the last instance of your App Widget is deleted from the App Widget host. This is where you should clean up any work done in
onEnabled(Context)
, such as delete a temporary database. - This is called for every broadcast and before each of the above callback methods. You normally don't need to implement this method because the default AppWidgetProvider implementation filters all App Widget broadcasts and calls the above methods as appropriate.
onUpdate()
onAppWidgetOptionsChanged()
onDeleted(Context, int[])
onEnabled(Context)
onDisabled(Context)
onReceive(Context, Intent)
The most important AppWidgetProvider callback is onUpdate()
because it is called when each App Widget is added to a host (unless you use a configuration Activity). If your App Widget accepts any user interaction events, then you need to register the event handlers in this callback. If your App Widget doesn't create temporary files or databases, or perform other work that requires clean-up, then onUpdate()
may be the only callback method you need to define. For example, if you want an App Widget with a button that launches an Activity when clicked, you could use the following implementation 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 of defining a PendingIntent
that launches an Activity
and attaching it to the App Widget's button with setOnClickPendingIntent(int, PendingIntent)
. Notice that it includes a loop that iterates through each entry inappWidgetIds
, which is an array of IDs that identify each App Widget created by this provider. In this way, if the user creates more than one instance of the App Widget, then they are all updated simultaneously. However, only one updatePeriodMillis
schedule will be managed for all instances of the App Widget. For example, if the update schedule is defined to be every two hours, and a second instance of the App Widget is added one hour after the first one, then they will both be updated on the period defined by the first one and the second update period will be ignored (they'll both be updated every two hours, not every hour).
Note: Because AppWidgetProvider
is an extension of BroadcastReceiver
, your process is not guaranteed to keep running after the callback methods return (see BroadcastReceiver
for information about the broadcast lifecycle). If your App Widget setup process can take several seconds (perhaps while performing web requests) and you require that your process continues, consider starting a Service
in the onUpdate()
method. From within the Service, you can perform your own updates to the App Widget without worrying about the AppWidgetProvider closing down due to an Application Not Responding (ANR) error. See the Wiktionary sample's AppWidgetProvider for an example of an App Widget running a Service
.
Also see the ExampleAppWidgetProvider.java sample class.
Receiving App Widget broadcast Intents
AppWidgetProvider
is just a convenience class. If you would like to receive the App Widget broadcasts directly, you can implement your ownBroadcastReceiver
or override the onReceive(Context, Intent)
callback. The Intents you need to care about are as follows:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
Pinning App Widgets
On devices running Android 8.0 (API level 26) and higher, the launchers that allow you to create pinned shortcuts also allow you to pin app widgets onto the launcher. Similar to pinned shortcuts, these pinned widgets give users access to specific tasks in your app.
In your app, you can create a request for the system to pin a widget onto a supported launcher by completing the following sequence of steps:
- Create the widget in your app's manifest file, as shown in the following snippet:
<manifest> ... <application> ... <receiver android:name="MyAppWidgetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_appwidget_info" /> </receiver> </application> </manifest>
- Call the
requestPinAddWidget()
method, as shown in the following code snippet:AppWidgetManager mAppWidgetManager = context.getSystemService(AppWidgetManager.class); ComponentName myProvider = new ComponentName(context, MyAppWidgetProvider.class); if (mAppWidgetManager.isRequestPinAppWidgetSupported()) { // Create the PendingIntent object only if your app needs to be notified // that the user allowed the widget to be pinned. Note that, if the pinning // operation fails, your app isn't notified. Intent pinnedWidgetCallbackIntent = new Intent( ... ); // Configure the intent so that your app's broadcast receiver gets // the callback successfully. This callback receives the ID of the // newly-pinned widget (EXTRA_APPWIDGET_ID). PendingIntent successCallback = PendingIntent.createBroadcast(context, 0, pinnedWidgetCallbackIntent); mAppWidgetManager.requestPinAppWidget(myProvider, null, successCallback); }
Note: If your app doesn't need to be notified of whether the system successfully pinned a widget onto a supported launcher, you can pass in null
as the third argument to requestPinAddWidget()
.
Creating an App Widget Configuration Activity
If you would like the user to configure settings when he or she adds a new App Widget, you can create an App Widget configuration Activity. This Activity
will be automatically launched by the App Widget host and allows the user to configure available settings for the App Widget at create-time, such as the App Widget color, size, update period or other functionality settings.
The configuration Activity should be declared as a normal Activity in the Android manifest file. However, it will be launched by the App Widget host with the ACTION_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 the android:configure
attribute (see Adding the AppWidgetProviderInfo Metadata above). For example, the configuration Activity can 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 you need is the actual Activity. There are, however, two important things to remember when you implement the Activity:
- The App Widget host calls the configuration Activity and the configuration Activity 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 extras as
EXTRA_APPWIDGET_ID
). - The
onUpdate()
method will not be called when the App Widget is created (the system will not send the ACTION_APPWIDGET_UPDATE broadcast when a configuration Activity is launched). It is the responsibility of the configuration Activity to request an update from the AppWidgetManager when the App Widget is first created. However,onUpdate()
will be called for subsequent updates—it is only skipped the first time.
See the code snippets in the following section for an example of how to return a result from the configuration and update the App Widget.
Updating the App Widget from the configuration Activity
When an App Widget uses a configuration Activity, it is the responsibility of the Activity to update the App Widget when configuration is complete. You can do so by requesting an update directly from the AppWidgetManager
.
Here's a summary of the procedure to properly update the App Widget and close the configuration Activity:
- 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); }
- Perform your App Widget configuration.
- When the configuration is complete, get an instance of the AppWidgetManager by calling
getInstance(Context)
:AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- Update the App Widget with a
RemoteViews
layout by callingupdateAppWidget(int, RemoteViews)
:RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); appWidgetManager.updateAppWidget(mAppWidgetId, views);
- 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();
Tip: When your configuration Activity first opens, set the Activity result to RESULT_CANCELED, along with EXTRA_APPWIDGET_ID, as shown in step 5 above. This way, if the user backs-out of the Activity before reaching the end, the App Widget host is notified that the configuration was cancelled and the App 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 a preview of what the app widget looks like. This preview is shown to the user from the widget picker. If this field is not supplied, the app widget's icon is used for the 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 Android emulator includes an application called "Widget Preview." To create a preview image, launch this application, select the app widget for your application and set it up how you'd like your preview image to appear, then save it 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 App Widgets use the RemoteViewsService
to display collections that are backed by remote data, such as from a content provider. The data provided by the RemoteViewsService
is presented in the app widget using one of the following view types, which we’ll refer to as “collection views:”
- A view that shows items in a vertically scrolling list. For an example, see the Gmail app widget.
- A view that shows items in two-dimensional scrolling grid. For an example, see the Bookmarks app widget.
- A stacked card view (kind of like a rolodex), where the user can flick the front card up/down to see the previous/next card, respectively. Examples include the YouTube and Books app widgets.
-
An adapter-backed simple
ViewAnimator
that animates between two or more views. Only one child is shown at a time.
ListView
GridView
StackView
AdapterViewFlipper
As stated above, these collection views display collections backed by remote data. This means that they use an Adapter
to bind their user interface to their data. An Adapter
binds individual items from a set of data into individual View
objects. Because these collection views are backed by adapters, the Android framework must include extra architecture to support their use in app widgets. In the context of an app widget, the Adapter
is replaced by aRemoteViewsFactory
, which is simply a thin wrapper around the Adapter
interface. When requested for a specific item in the collection, the RemoteViewsFactory
creates and returns the item for the collection as a RemoteViews
object. In order to include a collection view in your app widget, you must implement RemoteViewsService
and RemoteViewsFactory
.
RemoteViewsService
is a service that allows a remote adapter to request RemoteViews
objects. RemoteViewsFactory
is an interface for an adapter between a collection view (such as ListView
, GridView
, and so on) and the underlying data for that view. From the StackView Widget sample, 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 Widget sample:
This sample consists of a stack of 10 views, which display the values "0!"
through "9!"
The sample app widget has these primary behaviors:
- The user can vertically fling the top view in the app widget to display the next or previous view. This is a built-in StackView behavior.
- Without any user interaction, the app widget automatically advances through its views in sequence, like a slide show. This is due to the setting
android:autoAdvanceViewId="@id/stack_view"
in theres/xml/stackwidgetinfo.xml
file. This setting applies to the view ID, 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 view n," where n is the index (position) of the touched view. For more discussion of how this is implemented, see Adding 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 the additional steps you need to perform to implement an app widget with collections.
Manifest for app widgets with collections
In addition to the requirements listed in Declaring an app widget in the Manifest, to make it possible for app widgets with collections to bind to your RemoteViewsService
, you must declare the service in your manifest file with the permission BIND_REMOTEVIEWS
. This prevents other applications from freely accessing your app widget's data. For example, when creating an App Widget that uses RemoteViewsService
to populate a collection 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 of RemoteViewsService
.
Layout for app widgets with collections
The main requirement for your app widget layout XML file is that it include one of the collection views: ListView
, GridView
, StackView
, orAdapterViewFlipper
. Here is the widget_layout.xml
for the StackView Widget 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 the empty view represents empty state.
In addition to the layout file for your entire app widget, you must create another layout file that defines the layout for each item in the collection (for example, a layout for each book in a collection of books). For example, the StackView Widget sample only has one layout file, widget_item.xml
, since all items use the same layout. But the WeatherListWidget sample has two layout files: dark_widget_item.xml
and light_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 in onUpdate()
. The major difference in your implementation for onUpdate()
when creating an app widget with collections is that you must call setRemoteAdapter()
. This tells the collection view where to get its data. The RemoteViewsService
can then return your implementation of RemoteViewsFactory
, and the widget can serve up the appropriate data. When you call this method, you must pass an intent that points to your implementation of RemoteViewsService
and the app widget ID that specifies the app widget to update.
For example, here's how the StackView Widget sample implements the onUpdate()
callback method to set the RemoteViewsService
as the remote adapter for the app widget collection:
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
Persisting data
You can’t rely on a single instance of your service, or any data it contains, to persist. You should therefore not store any data in your RemoteViewsService
(unless it is static). If you want your app widget’s data to persist, the best approach is to use a ContentProvider
whose data persists beyond the process lifecycle.
As described above, your RemoteViewsService
subclass provides the RemoteViewsFactory
used to populate the remote collection view.
Specifically, you need to perform these steps:
- Subclass
RemoteViewsService
.RemoteViewsService
is the service through which a remote adapter can requestRemoteViews
. - In your
RemoteViewsService
subclass, include a class that implements theRemoteViewsFactory
interface.RemoteViewsFactory
is an interface for an adapter between a remote collection view (such asListView
,GridView
, and so on) and the underlying data for that view. Your implementation is responsible for making aRemoteViews
object for each item in the data set. This interface is a thin wrapper aroundAdapter
.
The primary contents of the RemoteViewsService
implementation is its RemoteViewsFactory
, described below.
RemoteViewsFactory interface
Your custom class that implements the RemoteViewsFactory
interface provides the app widget with the data for the items in its collection. To do 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 Widget sample, the data source is an array of WidgetItems
. The RemoteViewsFactory
functions as an adapter to glue the data to the remote collection view.
The two most important methods you need to implement for your RemoteViewsFactory
subclass are onCreate()
and getViewAt()
.
The system calls onCreate()
when creating your factory for the first time. This is where you set up any connections and/or cursors to your data source. For example, the StackView Widget sample uses onCreate()
to initialize an array of WidgetItem
objects. When your app widget is active, the system accesses these objects using their index position in the array and the text they contain is displayed.
Here is an excerpt from the StackView Widget sample's RemoteViewsFactory
implementation that shows portions of the onCreate()
method:
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 + "!")); } ... } ...
The RemoteViewsFactory
method getViewAt()
returns a RemoteViews
object corresponding to the data at the specified position
in the data set. Here is an excerpt from the StackView Widget sample's RemoteViewsFactory
implementation:
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 widget collection. But what if you want to add dynamic behavior to the individual items in your collection view?
As described in Using the AppWidgetProvider Class, you normally use setOnClickPendingIntent()
to set an object's click behavior—such as to cause a button to launch an Activity
. But this approach is not allowed for child views in an individual collection item (to clarify, you could use setOnClickPendingIntent()
to set up a global button in the Gmail app widget that launches the app, for example, but not on the individual list items). Instead, to add click behavior to individual items in a collection, you use setOnClickFillInIntent()
. This entails setting up a pending intent template for your collection view, and then setting a fill-in intent on each item in the collection via your RemoteViewsFactory
.
This section uses the StackView Widget sample to describe how to add behavior to individual items. In the StackView Widget sample, if the user touches the top view, the app widget displays the Toast
message "Touched view n," where n is the index (position) of the touched view. This is how it works:
- The
StackWidgetProvider
(anAppWidgetProvider
subclass) creates a pending intent that has a custom action calledTOAST_ACTION
. - When the user touches a view, the intent is fired and it broadcasts
TOAST_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 collection items is provided by theRemoteViewsFactory
, via theRemoteViewsService
.
Note: The StackView Widget sample uses a broadcast, but typically an app widget would simply launch an activity 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 the individual items set a fill-in intent to create unique behavior on an item-by-item basis.
This class also receives the broadcast that is sent when the user touches a view. It processes this event in its onReceive()
method. If the intent's action is TOAST_ACTION
, the app widget displays a Toast
message 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 given item. The fill-in intent is then combined with the PendingIntent
template in order to determine the final intent that will 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 that uses collections when updates occur. It shows how the app widget code interacts with the RemoteViewsFactory
, and how you can trigger updates:
![](https://developer.android.com/images/appwidgets/appwidget_collections.png)
One feature of app widgets that use collections is the ability to provide users with up-to-date content. For example, consider the Android 3.0 Gmail app widget, which provides users with a snapshot of their inbox. To make this possible, you need to be able to trigger your RemoteViewsFactory
and collection view to fetch and display new data. You achieve this with the AppWidgetManager
call notifyAppWidgetViewDataChanged()
. This call results in a callback to your RemoteViewsFactory
’s onDataSetChanged()
method, which gives you the opportunity to fetch any new data. Note that you can perform processing-intensive operations synchronously within the onDataSetChanged()
callback. You are guaranteed that this call will be completed before the metadata or view data is fetched from the RemoteViewsFactory
. In addition, you can perform processing-intensive operations within the getViewAt()
method. If this call takes a long time, the loading view (specified by the RemoteViewsFactory
’s getLoadingView()
method) will be displayed in the corresponding position of the collection view until it returns.
译文:
应用程序小部件
应用程序小部件是微型应用程序视图,可以嵌入到其他应用程序(如主屏幕)中,并接收定期更新。这些视图在用户界面中称为Widget,您可以使用App Widget提供程序发布视图。能够容纳其他App Widget的应用程序组件被称为App Widget主机。以下屏幕截图显示了音乐应用程序小部件。
![](https://developer.android.com/images/appwidgets/appwidget.png)
本文档介绍如何使用App Widget提供程序发布App Widget。有关创建自己的AppWidgetHost
应用程序小部件的讨论,请参阅 应用程序小部件主机。
小部件设计
有关如何设计应用程序小部件的信息,请阅读小部件设计指南。
基础
要创建一个App Widget,您需要以下内容:
- 描述App Widget的元数据,例如App Widget的布局,更新频率和AppWidgetProvider类。这应该在XML中定义。
- 定义允许您以编程方式基于广播事件与App Widget进行接口的基本方法。通过它,你将收到广播的App Widget更新,启用,禁用和删除。 查看布局
- 定义在XML中定义的App Widget的初始布局。
AppWidgetProviderInfo
目的
AppWidgetProvider
类实现
另外,您可以实施App Widget配置活动。这是一个可选项 Activity
,当用户添加您的App Widget时允许他或她在创建时修改App Widget设置。
以下部分介绍如何设置每个组件。
在清单中声明App Widget
首先,AppWidgetProvider
在申请AndroidManifest.xml
文件中声明这个类 。例如:
< 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>
的<receiver>
元素需要 android:name
属性,该属性指定了AppWidgetProvider
所使用的应用程序的widget。
该<intent-filter>
元素必须包含<action>
具有该android:name
属性的 元素。该属性指定AppWidgetProvider
接受ACTION_APPWIDGET_UPDATE
广播。这是您必须明确声明的唯一广播。根据需要AppWidgetManager
自动将所有其他App Widget广播发送到AppWidgetProvider。
该<meta-data>
元素指定的 AppWidgetProviderInfo
资源,需要以下属性:
android:name
- 指定元数据名称。使用android.appwidget.provider
标识数据的AppWidgetProviderInfo
描述符。android:resource
- 指定AppWidgetProviderInfo
资源位置。
添加AppWidgetProviderInfo元数据
所述AppWidgetProviderInfo
一个应用程序的widget,的基本品质定义如它的最小布局尺寸,其初始布局资源,多久更新应用窗口小部件,和(任选地)一个配置Activity推出在创建时间。使用单个<appwidget-provider>
元素在XML资源中定义AppWidgetProviderInfo对象, 并将其保存在项目的 res/xml/
文件夹中。
例如:
<appwidget提供商的xmlns:机器人= “http://schemas.android.com/apk/res/android” 机器人:minWidth = “40dp” 机器人:了minHeight = “40dp” 机器人:updatePeriodMillis = “86400000” 机器人:previewImage = “@ drawable / preview” android:initialLayout = “@ layout / example_appwidget” android:configure = “com.example.android.ExampleAppWidgetConfigure” android:resizeMode = “horizontal | vertical” android:widgetCategory = “home_screen” > </ appwidget提供商>
以下是<appwidget-provider>
属性的摘要:
minWidth
和minHeight
属性的值指定了App Widget 默认使用的最小空间量 。默认主屏幕基于具有定义的高度和宽度的单元网格来在其窗口中定位App Widgets。如果App Widget的最小宽度或高度值与单元格的尺寸不匹配,则App Widget尺寸向上舍 入到最接近的单元格尺寸。有关调整App Widgets的更多信息,请参阅 App Widget设计指南。
注意:要使您的应用程序小部件可跨设备移植,应用程序小部件的最小大小决不能大于4 x 4个单元格。
- 将
minResizeWidth
和minResizeHeight
属性指定应用Widget的绝对最小尺寸。这些值应该指定的大小低于该App Widget将难以辨认或不可用。使用这些属性允许用户调整窗口小部件的大小,使其大小可能小于minWidth
和由minHeight
属性定义的默认窗口大小 。在Android 3.1中引入。有关调整App Widgets的更多信息,请参阅 App Widget设计指南。
- 该
updatePeriodMillis
属性定义了App Widget框架AppWidgetProvider
通过调用onUpdate()
回调方法来请求更新的频率。实际更新不能保证按照这个值准确发生,我们建议尽可能不要频繁更新 - 也许每小时不要超过一次,以节约电池。您也可以允许用户调整配置中的频率 - 有些人可能希望每15分钟更新股票代码,或者每天只能更新四次。注意:如果设备在进行更新时(如定义的那样
updatePeriodMillis
)睡眠,则设备将被唤醒以执行更新。如果您每小时不更新一次以上,这可能不会导致电池寿命的重大问题。但是,如果需要更频繁地更新和/或在设备处于睡眠状态时不需要更新,则可以根据不会唤醒设备的警报来执行更新。为此,请使用“设置”来设置您的AppWidgetProvider收到的Intent的警报AlarmManager
。设置闹钟类型为“ELAPSED_REALTIME
或”RTC
,只有在设备唤醒时才会发出闹钟。然后设置updatePeriodMillis
为零("0"
)。 - 该
initialLayout
属性指向定义App Widget布局的布局资源。 - 该
configure
属性定义在Activity
用户添加App Widget时启动,以便他或她配置App Widget属性。这是可选的(请阅读下面的创建应用程序Widget配置活动)。 - 该
previewImage
属性指定应用程序窗口小部件配置后的外观样式的预览,用户在选择应用窗口小部件时看到的内容。如果没有提供,用户会看到您的应用程序的启动器图标。该字段对应 于文件中元素的android:previewImage
属性。有关使用的更多讨论,请参阅设置预览图像。在Android 3.0中引入。<receiver>
AndroidManifest.xml
previewImage
- 该
autoAdvanceViewId
属性指定应由widget主机自动进阶的app widget子视图的视图ID。在Android 3.0中引入。 - 该
resizeMode
属性指定了小部件可以调整大小的规则。您可以使用此属性来调整主屏幕小部件的大小 - 水平,垂直或两个轴。用户按住一个小部件以显示其大小调整手柄,然后拖动水平和/或垂直手柄以更改布局网格上的大小。resizeMode
属性的值 包括“水平”,“垂直”和“无”。要声明一个小部件可以水平和垂直调整大小,请提供值“horizontal | vertical”。在Android 3.1中引入。 - 该
minResizeHeight
属性指定小部件可以调整到的最小高度(以dps为单位)。如果此字段大于minHeight
或未启用垂直调整大小,则此字段无效(请参阅resizeMode
)。在Android 4.0中引入。 - 该
minResizeWidth
属性指定了小部件可以调整到的最小宽度(以dps为单位)。如果此字段大于minWidth
或未启用水平调整大小,则此字段无效(请参阅resizeMode
)。在Android 4.0中引入。 - 该
widgetCategory
属性声明您的App Widget是否可以显示在主屏幕(home_screen
),锁定屏幕(keyguard
)或两者上。只有低于5.0的Android版本才支持锁屏小部件。对于Android 5.0及更高版本,只有home_screen
有效。
有关元素AppWidgetProviderInfo
接受的属性的更多信息,请参阅类<appwidget-provider>
。
创建应用程序窗口小部件布局
您必须使用XML定义App Widget的初始布局,并将其保存在项目的 res/layout/
目录中。您可以使用下面列出的View对象来设计您的App Widget,但在开始设计App Widget之前,请阅读并理解 App Widget设计指南。
如果您熟悉布局,创建App Widget布局很简单。但是,您必须知道,App Widget布局是基于RemoteViews
,不支持每种布局或视图窗口小部件。
RemoteViews对象(以及因此的App Widget)可以支持以下布局类:
以下小部件类:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
这些类的后代不支持。
RemoteViews还支持ViewStub
,这是一个不可见的,零大小的视图,您可以使用它在运行时延迟布局资源。
为应用小部件添加边距
小部件通常不应该扩展到屏幕边缘,并且不应该在视觉上与其他小部件齐平,所以您应该在小部件框架的周围添加边距。
从Android 4.0开始,应用程序窗口小部件会自动在窗口小部件框架和应用程序窗口小部件的边界框之间填充,以便与用户主屏幕上的其他窗口小部件和图标更好地对齐。要利用这个强烈建议的行为,请将您的应用程序的targetSdkVersion设置为14或更大。
编写一个适用于早期版本平台的自定义边距的单一布局很容易,而Android 4.0和更高版本没有额外的边距:
- 将您的应用程序设置
targetSdkVersion
为14或更大。 - 创建一个如下所示的布局,为其边距引用维度资源:
<的FrameLayout 机器人:layout_width = “match_parent” 机器人:layout_height = “match_parent” 机器人:填充= “@扪/ widget_margin” > <LinearLayout中机器人:layout_width = “match_parent” 机器人:layout_height = “match_parent” 机器人:取向= “水平” android:background = “@ drawable / my_widget_background” > ... </ LinearLayout> </ FrameLayout>
- 创建两个维度资源,一个
res/values/
用于提供Android 4.0之前的自定义边距,另一个res/values-v14/
用于为Android 4.0 widgets提供额外的填充:res / values / dimens.xml:
< dimen name = “widget_margin” > 8dp </ dimen >
res / values-v14 / dimens.xml:
< dimen name = “widget_margin” > 0dp </ dimen >
另一种选择是在默认情况下简单地为您的九块补丁背景资产构建额外的边距,并为API级别14或更高版本提供不同的九块补丁,而无边距。
使用AppWidgetProvider类
您必须使用<receiver>
AndroidManifest中的元素将您的AppWidgetProvider类实现声明为广播接收者(请参阅 上面清单中的声明应用程序Widget)。
本AppWidgetProvider
类广播接收器作为一个方便的类来处理应用的Widget广播延伸。AppWidgetProvider只接收与App Widget相关的事件广播,比如当App Widget被更新,删除,启用和禁用时。发生这些广播事件时,AppWidgetProvider会收到以下方法调用:
-
这被称为以
updatePeriodMillis
AppWidgetProviderInfo中的属性定义的间隔更新App Widget (请参阅上面的 添加AppWidgetProviderInfo元数据 )。当用户添加App Widget时,也会调用这个方法,所以它应该执行必要的设置,比如为Views定义事件处理程序,并Service
在必要时启动一个临时的 。但是,如果您声明了配置Activity, 则 在用户添加App Widget时不会调用此方法,但会为后续更新调用此方法。配置完成后,执行第一次更新是配置活动的责任。(请参阅下面的 创建应用程序小部件配置活动 。) -
这是在小部件第一次放置时以及小部件被调整大小时调用的。您可以使用此回调来显示或隐藏基于窗口小部件大小范围的内容。您通过调用来获取大小范围
getAppWidgetOptions()
,返回的值Bundle
包括以下内容:
OPTION_APPWIDGET_MIN_WIDTH
- 包含小部件实例的当前宽度(以dp为单位)的下限。OPTION_APPWIDGET_MIN_HEIGHT
- 以小部件实例的当前高度(以dp为单位)包含下限。OPTION_APPWIDGET_MAX_WIDTH
- 以小部件实例的当前宽度(以dp为单位)包含上限。OPTION_APPWIDGET_MAX_HEIGHT
- 以小部件实例的当前宽度(以dp为单位)包含上限。
- 每当从App Widget主机中删除App Widget时,都会调用它。
- 这是第一次创建App Widget的实例时调用的。例如,如果用户添加了App Widget的两个实例,这只是第一次调用。如果您需要打开一个新的数据库或执行其他设置,只需要为所有App Widget实例执行一次,那么这是一个很好的选择。
-
当您的App Widget的最后一个实例从App Widget主机中被删除时,这被调用。这是你应该清理任何工作
onEnabled(Context)
,如删除临时数据库。 - 这被称为每个广播和每个上述回调方法之前。您通常不需要实现此方法,因为默认的AppWidgetProvider实现会过滤所有App Widget广播,并根据需要调用上述方法。
onUpdate()
onAppWidgetOptionsChanged()
onDeleted(Context, int[])
onEnabled(Context)
onDisabled(Context)
onReceive(Context, Intent)
最重要的AppWidgetProvider回调函数是 onUpdate()
因为当每个App Widget添加到主机时调用它(除非使用配置Activity)。如果您的App Widget接受任何用户交互事件,则需要在此回调中注册事件处理程序。如果您的App Widget不创建临时文件或数据库,或执行其他需要清理的工作,则 onUpdate()
可能是您需要定义的唯一回调方法。例如,如果你想要一个带有一个按钮的App Widget,点击后可以启动一个Activity,你可以使用下面的AppWidgetProvider实现:
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate (Context context ,AppWidgetManager appWidgetManager ,int [] appWidgetIds ){ final int N = appWidgetIds 。长度; //执行对于属于此提供各应用程序的widget这个循环程序为(诠释我= 0 ; 我< Ñ ; 我++){ int appWidgetId = appWidgetIds [ i ]; //创建一个Intent推出为ExampleActivity 意图意图= 新意图(上下文,为ExampleActivity 。类); PendingIntent pendingIntent = PendingIntent 。getActivity (context ,0 ,intent ,0 ); //获取App Widget的布局,并将一个单击监听器//添加到按钮RemoteViews views = new RemoteViews (上下文。getPackageName (),- [R 。布局。appwidget_provider_layout ); 意见。setOnClickPendingIntent (ř 。ID 。按钮,的PendingIntent ); //告诉AppWidgetManager在当前的app小部件 appWidgetManager 上执行更新。updateAppWidget (appWidgetId ,views ); } } }
这个AppWidgetProvider只 onUpdate()
定义了一个方法来定义一个PendingIntent
启动Activity
并附加到App Widget的按钮setOnClickPendingIntent(int, PendingIntent)
。请注意,它包含一个循环遍历每个条目 appWidgetIds
,这是一个ID数组,标识由此提供者创建的每个App Widget。这样,如果用户创建了多个App Widget的实例,那么它们都是同时更新的。但是,只有一个updatePeriodMillis
时间表将被管理的所有应用程序小部件的实例。例如,如果更新时间表定义为每两个小时,并且在第一个小时之后一个小时添加App Widget的第二个实例,则它们将在由第一个和第二个更新定义的时间段期间将被忽略(他们将每两小时更新一次,而不是每一小时)。
注意:因为AppWidgetProvider
是扩展名BroadcastReceiver
,所以在回调方法返回后,您的进程不保证继续运行(请参阅BroadcastReceiver
有关广播生命周期的信息)。如果您的App Widget安装过程可能需要几秒钟(可能在执行Web请求时),并且您需要继续进行,请考虑Service
在该onUpdate()
方法中启动一个。从服务内部,您可以执行您自己的App Widget更新,而不用担心由于Application Not Responding(ANR)错误导致AppWidgetProvider关闭。请参阅Wiktionary示例的AppWidgetProvider,以获取运行a的App Widget示例Service
。
另请参阅ExampleAppWidgetProvider.java 示例类。
接收App Widget广播意图
AppWidgetProvider
只是一个便利的班级。如果您想直接接收App Widget广播,则可以实现您自己的 BroadcastReceiver
或覆盖 onReceive(Context, Intent)
回调。你需要关心的意图如下:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
钉住应用程序小部件
在运行Android 8.0(API级别26)或更高版本的设备上,允许您创建固定快捷方式的启动器也允许您将应用程序控件固定到启动器上。与固定的快捷方式类似,这些固定的小部件可让用户访问应用中的特定任务。
在您的应用程序中,您可以创建一个系统请求,通过完成以下一系列步骤将小部件固定到支持的启动器上:
- 在应用程序的清单文件中创建小部件,如下面的代码片段所示:
<manifest> ... <application> ... <receiver android:name = “MyAppWidgetProvider” > <intent-filter> <action android:name = “android.appwidget.action.APPWIDGET_UPDATE” /> </ intent-filter> <meta-data android:name = “android.appwidget.provider” android:resource = “@ xml / my_appwidget_info” /> </ receiver> </ application> </ manifest>
- 调用该
requestPinAddWidget()
方法,如以下代码片段所示:AppWidgetManager mAppWidgetManager = context.getSystemService(AppWidgetManager.class); ComponentName myProvider = new ComponentName(context, MyAppWidgetProvider.class); if (mAppWidgetManager.isRequestPinAppWidgetSupported()) { // Create the PendingIntent object only if your app needs to be notified // that the user allowed the widget to be pinned. Note that, if the pinning // operation fails, your app isn't notified. Intent pinnedWidgetCallbackIntent = new Intent( ... ); // Configure the intent so that your app's broadcast receiver gets // the callback successfully. This callback receives the ID of the // newly-pinned widget (EXTRA_APPWIDGET_ID). PendingIntent successCallback = PendingIntent.createBroadcast(context, 0, pinnedWidgetCallbackIntent); mAppWidgetManager.requestPinAppWidget(myProvider, null, successCallback); }
注意:如果您的应用程序不需要通知系统是否成功地将小部件固定到受支持的启动器上,则可以将其null
作为第三个参数 传入requestPinAddWidget()
。
创建一个App Widget配置活动
如果您希望用户在添加新App Widget时配置设置,则可以创建App Widget配置活动。这Activity
将由App Widget主机自动启动,并允许用户在创建时配置App Widget的可用设置,例如App Widget颜色,大小,更新周期或其他功能设置。
配置Activity应该在Android清单文件中声明为普通的Activity。但是,它将由App Widget主机通过ACTION_APPWIDGET_CONFIGURE
动作来启动,所以Activity需要接受这个Intent。例如:
<activity android:name = “.ExampleAppWidgetConfigure” > <intent-filter> <action android:name = “android.appwidget.action.APPWIDGET_CONFIGURE” /> </ intent-filter> </ activity>
此外,必须在AppWidgetProviderInfo XML文件中使用android:configure
属性(请参阅上面的添加AppWidgetProviderInfo元数据)来声明Activity 。例如,配置Activity可以像这样声明:
<appwidget-provider xmlns:android = “http://schemas.android.com/apk/res/android” ... android:configure = “com.example.android.ExampleAppWidgetConfigure” ... > </ appwidget-provider >
请注意,该活动是使用完全限定的名称空间声明的,因为它将从包的范围之外引用。
这就是您需要开始使用配置活动的全部内容。现在你需要的是实际的活动。但是,在实施“活动”时需要记住两件重要的事情:
- App Widget主机调用配置Activity,配置Activity应始终返回结果。结果应该包括启动Activity的Intent传递的App Widget ID(保存在Intent extras中
EXTRA_APPWIDGET_ID
)。 - App Widget创建时不会调用该
onUpdate()
方法(当配置Activity启动时,系统不会发送ACTION_APPWIDGET_UPDATE广播)。当App Widget第一次创建时,配置Activity的职责是从AppWidgetManager请求更新。但是, 将被要求后续更新 - 它只是第一次跳过。onUpdate()
有关如何从配置返回结果并更新App Widget的示例,请参阅以下部分中的代码片段。
从配置Activity更新App Widget
当一个App Widget使用一个配置Activity时,当配置完成时,Activity是更新App Widget的责任。您可以通过直接请求更新来完成 AppWidgetManager
。
以下是正确更新App Widget并关闭配置Activity的过程摘要:
- 首先,从启动Activity的Intent中获取App Widget ID:
Intent intent = getIntent (); 捆绑extras = 意图。getExtras (); if (extras != null ){ mAppWidgetId = extras 。getInt (AppWidgetManager ,EXTRA_APPWIDGET_ID ,AppWidgetManager ,INVALID_APPWIDGET_ID ); }
- 执行你的App Widget配置。
- 配置完成后,调用
getInstance(Context)
以下命令获取AppWidgetManager的一个实例 :AppWidgetManager appWidgetManager = AppWidgetManager 。getInstance (context );
RemoteViews
通过调用updateAppWidget(int, RemoteViews)
以下布局来 更新App Widget :RemoteViews 视图= 新RemoteViews (上下文。getPackageName (),- [R 。布局。example_appwidget ); appWidgetManager 。updateAppWidget (mAppWidgetId ,views );
- 最后,创建返回意图,将其设置为活动结果,并完成活动:
意图resultValue = 新的意图(); resultValue 。putExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID ,mAppWidgetId ); setResult (RESULT_OK ,resultValue ); 完成();
提示:首次打开您的配置活动时,将活动结果设置为RESULT_CANCELED,连同EXTRA_APPWIDGET_ID,如上面的步骤5所示。这样,如果用户在到达结尾之前退出Activity,则会通知App Widget主机取消配置,并且不会添加App Widget。
以ApiDemos中的ExampleAppWidgetConfigure.java示例类为例。
设置预览图像
Android 3.0引入了该previewImage
字段,该字段指定了应用程序小部件外观的预览。这个预览会从窗口部件选择器向用户显示。如果未提供此字段,则应用程序小部件的图标用于预览。
这是你如何在XML中指定这个设置:
<appwidget-provider xmlns:android = “http://schemas.android.com/apk/res/android” ... android:previewImage = “@ drawable / preview” > </ appwidget-provider>
为了帮助为您的应用程序小部件创建预览图像(在previewImage
字段中指定),Android模拟器包含一个名为“小部件预览”的应用程序。要创建预览图像,请启动此应用程序,为应用程序选择应用程序小部件,并设置预览图像的显示方式,然后将其保存并放入应用程序的可绘制资源中。
与集合使用应用程序窗口小部件
Android 3.0引入了应用程序小部件和集合。这些类型的App Widget使用RemoteViewsService
显示由远程数据(例如来自内容提供者)支持的集合。由RemoteViewsService
应用程序小部件提供的数据使用以下视图类型之一,我们将其称为“集合视图:”
- 显示垂直滚动列表中的项目的视图。有关示例,请参阅Gmail应用程序小部件。
- 显示二维滚动网格中的项目的视图。有关示例,请参阅书签应用程序小部件。
- 一个堆叠的卡片视图(有点像rolodex),用户可以分别上下滑动前面的卡片来查看上一张/下一张卡片。示例包括YouTube和图书应用程序小部件。
-
支持适配器的简单的
ViewAnimator
动画在两个或更多的视图之间。一次只显示一个孩子。
ListView
GridView
StackView
AdapterViewFlipper
如上所述,这些集合视图显示由远程数据支持的集合。这意味着他们使用一个Adapter
将他们的用户界面绑定到他们的数据。一个Adapter
结合从一组数据转换成单独的个别项目View
的对象。由于这些集合视图由适配器支持,所以Android框架必须包含额外的架构以支持其在应用程序小部件中的使用。在应用程序小部件的上下文中,Adapter
被替换为a RemoteViewsFactory
,这仅仅是Adapter
界面周围的一个薄包装。当在集合中请求特定项目时,RemoteViewsFactory
创建并将集合的项目作为RemoteViews
对象返回。为了在您的应用程序小部件中包含集合视图,您必须实现RemoteViewsService
和RemoteViewsFactory
。
RemoteViewsService
是一种允许远程适配器请求RemoteViews
对象的服务。RemoteViewsFactory
是集合视图(例如ListView
,GridView
等)与该视图的基础数据之间的适配器的接口。从 StackView Widget示例中,下面是用于实现此服务和接口的样板代码示例:
公共类StackWidgetService 扩展RemoteViewsService { @覆盖公共RemoteViewsFactory onGetViewFactory (意向意图){ 返回新StackRemoteViewsFactory (此。getApplicationContext (),意向); } } 类StackRemoteViewsFactory 实现RemoteViewsService 。RemoteViewsFactory { // ...在这里包含适配器类的方法。请参阅StackView Widget示例。}
示例应用程序
本节中的代码摘录来自StackView Widget示例:
该样品由10个视图,其显示值的堆叠的 "0!"
通过"9!"
将样品应用部件有这些主要行为:
- 用户可以垂直滑动应用程序窗口小部件中的顶视图,以显示下一个或上一个视图。这是一个内置的StackView行为。
- 在没有任何用户交互的情况下,应用程序窗口小部件会按顺序自动前进,例如幻灯片放映。这是由于
android:autoAdvanceViewId="@id/stack_view"
在res/xml/stackwidgetinfo.xml
文件中的设置 。该设置适用于视图ID,在这种情况下,视图ID是堆栈视图的视图ID。 - 如果用户触摸顶视图,则应用小部件显示
Toast
消息“触摸视图n ”,其中 n是所触摸的视图的索引(位置)。有关如何实现的更多讨论,请参阅 将行为添加到单个项目。
使用集合实现应用程序小部件
要使用集合实现应用程序窗口小部件,您需要遵循与用于实现任何应用程序窗口小部件相同的基本步骤。以下部分描述了您需要执行的附加步骤来实现具有集合的应用程序窗口小部件。
清单与集合的应用程序窗口小部件
除了在清单中声明应用程序窗口小部件中列出的要求之外,为了使集合的应用程序窗口小部件可以绑定到您的应用程序窗口RemoteViewsService
,您必须在清单文件中使用该许可声明该服务BIND_REMOTEVIEWS
。这可以防止其他应用程序自由访问您的应用程序窗口部件的数据 例如,在创建RemoteViewsService
用于填充集合视图的App Widget时,清单条目可能如下所示:
<service android:name = “MyWidgetService” ... android:permission = “android.permission.BIND_REMOTEVIEWS” />
该行android:name="MyWidgetService"
指的是你的子类RemoteViewsService
。
具有集合的应用程序小部件的布局
为您的应用程序窗口小部件的布局XML文件中的主要要求是,它包括收集意见之一:ListView
, GridView
,StackView
,或 AdapterViewFlipper
。这里是 widget_layout.xml
为StackView窗口小部件样本:
<?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: = “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” 机器人:重力= “中心” 机器人:背景= “@绘制/ widget_item_background” 机器人:文字颜色= “#FFFFFF” 机器人:TEXTSTYLE = “黑体” 机器人:文本= “@串/ empty_view_text” 机器人: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
为应用程序窗口小部件集合的远程适配器:
公共空隙的onUpdate (语境上下文,AppWidgetManager appWidgetManager ,诠释[] appWidgetIds ){ //与远程适配器更新每个应用小部件的对(诠释我= 0 ; 我< appWidgetIds 。长度; ++ 我){ //设置启动StackViewService的意图,它将//为这个集合提供视图。意图意图= 新的意图( 上下文,StackWidgetService 。类); //将应用部件ID添加到意图的演员。 意图。putExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID ,appWidgetIds [ 我]); 意图。使用setData (乌里。解析(意图。toUri (意向。URI_INTENT_SCHEME ))); //为应用程序小部件布局实例化RemoteViews对象。RemoteViews rv = 新的RemoteViews (上下文。getPackageName (),- [R 。布局。widget_layout ); //设置RemoteViews对象以使用RemoteViews适配器。//此适配器通过指定的意图连接到RemoteViewsService。//这是你如何填充数据。 rv 。setRemoteAdapter (appWidgetIds [ 我],[R 。ID 。stack_view ,意图); //当集合没有项目时,显示空的视图。 //它应该在用于实例化上面的RemoteViews //对象的同一个布局中。 rv 。setEmptyView (ř 。ID 。stack_view ,- [R 。ID 。empty_view ); / / / / 做额外的处理特定于这个应用程序部件... // appWidgetManager 。updateAppWidget (appWidgetIds [ i ],rv ); } 超。onUpdate (上下文,appWidgetManager ,appWidgetIds ); }
RemoteViewsService类
坚持数据
您不能依靠您的服务的单个实例或其包含的任何数据来坚持。因此,你不应该存储任何数据RemoteViewsService
(除非它是静态的)。如果您希望应用程序小部件的数据持续存在,那么最好的方法是使用ContentProvider
其数据在整个流程生命周期之外持续存在的数据。
如上所述,你的RemoteViewsService
子类提供了RemoteViewsFactory
用来填充远程集合视图。
具体来说,您需要执行以下步骤:
- 子类
RemoteViewsService
。RemoteViewsService
是远程适配器可以通过其请求的服务RemoteViews
。 - 在你的
RemoteViewsService
子类中,包含一个实现RemoteViewsFactory
接口的类。RemoteViewsFactory
是远程集合视图(例如ListView
,GridView
等)与该视图的基础数据之间的适配器的接口。您的实现负责为RemoteViews
数据集中的每个项目创建一个对象。这个界面是一个薄薄的包装Adapter
。
实施的主要内容RemoteViewsService
是RemoteViewsFactory
,如下所述。
RemoteViewsFactory接口
实现RemoteViewsFactory
接口的自定义类为应用程序小部件提供了集合中项目的数据。为此,它将您的应用程序窗口项目XML布局文件与数据源结合在一起。这个数据源可以是从数据库到简单数组的任何东西。在 StackView Widget示例中,数据源是一个数组WidgetItems
。RemoteViewsFactory
作为将数据粘贴到远程集合视图的适配器的功能。
你需要实现你的两个最重要的方法 RemoteViewsFactory
子类是 onCreate()
和 getViewAt()
。
系统onCreate()
在首次创建工厂时调用。这是您设置数据源的连接和/或游标的地方。例如,StackView Widget示例用于onCreate()
初始化一个WidgetItem
对象数组。当您的应用程序小部件处于活动状态时,系统将使用它们在数组中的索引位置访问这些对象,并显示它们包含的文本。
下面是StackView Widget 示例 RemoteViewsFactory
实现的摘录,显示了onCreate()
方法的一部分:
类StackRemoteViewsFactory 实现RemoteViewsService 。RemoteViewsFactory { 私有静态最终诠释mCount = 10 ; private List < WidgetItem > mWidgetItems = new ArrayList < WidgetItem >(); private Context mContext ; private int mAppWidgetId ; 公共StackRemoteViewsFactory (上下文上下文,意图意图) { mContext = context ; mAppWidgetId = 意图。getIntExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID ,AppWidgetManager 。INVALID_APPWIDGET_ID ); } public void onCreate (){ //在onCreate()中设置任何连接/游标到你的数据源。举重,/ / 例如下载或创建内容等,应推迟onDataSetChanged()/ / 或getViewAt()。在这个通话中超过20秒钟将导致一个ANR。for (int i。) = 0 ; 我< mCount ; 我++){ mWidgetItems 。add (new WidgetItem (i + “!” )); } ... } ...
该RemoteViewsFactory
方法getViewAt()
返回与数据集中RemoteViews
指定position
的数据对应的对象。以下是 StackView Widget示例RemoteViewsFactory
实现的摘录:
public RemoteViews getViewAt (int position ){ //根据应用程序构件项目XML文件构造远程视图项目,//根据位置设置文本。RemoteViews RV = 新RemoteViews (mContext 。getPackageName (),- [R 。布局。widget_item ); rv 。setTextViewText ([R 。ID 。widget_item ,mWidgetItems 。得到(位置)。 文字); ... //返回远程视图对象。返回rv ; }
将行为添加到单个项目
以上部分向您展示了如何将您的数据绑定到您的应用部件集合。但是如果你想添加动态行为到你的集合视图中的单个项目呢?
如使用AppWidgetProvider类所述,通常用于setOnClickPendingIntent()
设置对象的单击行为,例如导致按钮启动Activity
。但是,这种方法不允许在个人收藏项目中的子视图(为了说明,您可以使用setOnClickPendingIntent()
在Gmail应用程序小部件中设置全局按钮,例如启动应用程序,但不在单个列表项目中)。相反,要将点击行为添加到集合中的单个项目,您可以使用setOnClickFillInIntent()
。这需要为您的收藏视图设置待定的意图模板,然后通过您的收藏夹为收藏中的每个项目设置填充意图RemoteViewsFactory
。
本节使用StackView Widget示例来描述如何将行为添加到单个项目。在StackView Widget示例中,如果用户触摸顶部视图,则应用小部件将显示Toast
消息“触摸视图n ”,其中 n是触摸视图的索引(位置)。这是如何工作的:
- 该
StackWidgetProvider
(一个AppWidgetProvider
子类)创建待意图已调用自定义操作TOAST_ACTION
。 - 当用户触摸视图时,意图被触发并且广播
TOAST_ACTION
。 - 这个广播由所截取
StackWidgetProvider
的onReceive()
方法,以及应用程序插件播放Toast
所触摸的视图的信息。收集项目的数据是由RemoteViewsFactory
,通过RemoteViewsService
。
注:该StackView的Widget样品使用广播,但通常一个应用程序窗口小部件只会在这样一个情况下推出的活动。
设置未决的意向模板
该StackWidgetProvider
(AppWidgetProvider
子)建立了一个悬而未决的意图。集合中的个人物品不能建立自己的未决意图。相反,集合作为一个整体建立了一个未决的意图模板,并且各个项目设置了一个填充意图,以逐个项目为基础创建独特的行为。
此类还接收用户触摸视图时发送的广播。它在它的onReceive()
方法中处理这个事件。如果意图的动作是 TOAST_ACTION
,则应用小部件显示Toast
当前视图的消息。
公共类StackWidgetProvider 扩展AppWidgetProvider { 公共静态最终字符串TOAST_ACTION = “com.example.android.stackwidget.TOAST_ACTION” ; public static final String EXTRA_ITEM = “com.example.android.stackwidget.EXTRA_ITEM” ; ... //当BroadcastReceiver收到一个Intent广播时调用。//检查是否意图的动作是TOAST_ACTION。如果是,应用程序小部件//显示当前项目的Toast消息。@Override public void onReceive ( 上下文上下文,意图意图){ AppWidgetManager mgr = AppWidgetManager 。getInstance (context ); 如果(意图。的getAction ()。等号(TOAST_ACTION )){ INT appWidgetId = 意图。getIntExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID ,AppWidgetManager 。INVALID_APPWIDGET_ID ); int viewIndex = 意图。getIntExtra (EXTRA_ITEM ,0 ); 吐司。makeText (上下文,“已触摸视图” + viewIndex ,吐司。LENGTH_SHORT )。show (); } 超。onReceive (context ,intent ); } @Override public void onUpdate (上下文上下文,AppWidgetManager appWidgetManager ,int [] appWidgetIds ){ //更新每个与远程适配器的应用小部件的对(诠释我= 0 ; 我< appWidgetIds 。长度; ++ 我){ //设置向上指向将在StackViewService意图//提供这个集合的意见。意图意图= 新意图(上下文,StackWidgetService 。类); 意图。putExtra (AppWidgetManager 。 EXTRA_APPWIDGET_ID ,appWidgetIds [ i ]); //当intents被比较时,extras被忽略,所以我们需要在数据中嵌入额外的//这样extras就不会被忽略。 意图。使用setData (乌里。解析(意图。toUri (意向。URI_INTENT_SCHEME ))); RemoteViews RV = 新RemoteViews (上下文。getPackageName (),- [R 。布局。widget_layout ); rv 。setRemoteAdapter (appWidgetIds [ 我],[R 。ID 。stack_view ,意图); //当集合没有项目时,显示空的视图。它应该是集合视图的一个兄弟。 rv 。setEmptyView (ř 。ID 。stack_view ,- [R 。ID 。empty_view ); //这个部分可以使项目具有个性化的行为。 //这是通过设置一个未决的意图模板来实现的。集合中的个人物品//不能建立自己的未决意图。相反,整个集合设置一个未决的意图模板,并且各个项目设置一个fillInIntent //以在逐个项目的基础上创建独特的行为。意图toastIntent = 新意图(上下文,StackWidgetProvider 。类); //为意图设置动作。//当用户触摸一个特定的视图时,会产生//广播TOAST_ACTION 的效果。 toastIntent 。setAction ( StackWidgetProvider 。TOAST_ACTION ); toastIntent 。putExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID ,appWidgetIds [ 我]); 意图。使用setData (乌里。解析(意图。toUri (意向。URI_INTENT_SCHEME ))); PendingIntent toastPendingIntent = PendingIntent 。getBroadcast (上下文,0 ,toastIntent ,的PendingIntent 。FLAG_UPDATE_CURRENT ); rv 。setPendingIntentTemplate (ř 。ID 。stack_view ,toastPendingIntent ); appWidgetManager 。updateAppWidget (appWidgetIds [ i ],rv ); } 超。onUpdate (context ,appWidgetManager ,appWidgetIds ); } }
设置填充意图
您RemoteViewsFactory
必须为集合中的每个项目设置一个填充意图。这使得区分给定项目的单个点击动作成为可能。然后将填充意图与PendingIntent
模板组合,以确定在单击项目时将执行的最终意图。
公共类StackWidgetService 扩展RemoteViewsService { @覆盖公共RemoteViewsFactory onGetViewFactory (意向意图){ 返回新StackRemoteViewsFactory (此。getApplicationContext (),意向); } } 类StackRemoteViewsFactory 实现RemoteViewsService 。RemoteViewsFactory { 私有静态最终诠释mCount = 10 ; private List < WidgetItem > mWidgetItems = new ArrayList < WidgetItem >(); private Context mContext ; private int mAppWidgetId ; public StackRemoteViewsFactory (Context context ,Intent intent ){ mContext = context ; mAppWidgetId = 意图。getIntExtra (AppWidgetManager 。EXTRA_APPWIDGET_ID , AppWidgetManager 。INVALID_APPWIDGET_ID ); } //初始化数据集。公共无效onCreate (){ / /在onCreate()你设置任何连接/游标到您的数据源。举重,/ / 例如下载或创建内容等,应推迟onDataSetChanged()/ / 或getViewAt()。在这个通话中超过20秒钟将导致一个ANR。for (int i = 0 ; i < mCount ; i ++){ mWidgetItems 。添加(新的 WidgetItem (i + “!” )); } ... } ... //给定一个WidgetItem在数组中的位置(索引),使用该项目的文本值与应用控件项目XML文件结合来构造一个RemoteViews对象。public RemoteViews getViewAt (int position ){ // position将始终从0到getCount() - 1. //根据应用程序窗口项目XML文件构造RemoteViews项目,并根据位置设置//文本。RemoteViews rv = 新的RemoteViews (mContext 。 getPackageName (),R 。布局。widget_item ); rv 。setTextViewText (ř 。ID 。widget_item ,mWidgetItems 。得到(位置)。文本); //接下来,设置一个填充意图,它将被用来填充在StackWidgetProvider的集合视图中设置的未决意图模板。Bundle extras = new Bundle (); 临时演员。putInt (StackWidgetProvider 。EXTRA_ITEM ,位置); Intent fillInIntent = new Intent (); fillInIntent 。putExtras (extras ); //可以区分单个点击//给定项目 rv的动作。setOnClickFillInIntent (ř 。ID 。widget_item ,fillInIntent ); ... //返回RemoteViews对象。返回rv ; } ... }
保持收藏数据新鲜
下图说明了发生更新时使用集合的应用程序窗口小部件中发生的流程。它显示了应用程序小部件代码如何与之交互 RemoteViewsFactory
,以及如何触发更新:
![](https://developer.android.com/images/appwidgets/appwidget_collections.png)
使用集合的应用程序小部件的一个功能是能够为用户提供最新的内容。例如,考虑Android 3.0 Gmail应用程序小部件,它为用户提供收件箱的快照。为了使这成为可能,您需要能够触发您的RemoteViewsFactory
和收集视图来获取和显示新的数据。你AppWidgetManager
打电话来实现这一点notifyAppWidgetViewDataChanged()
。这个调用导致回调你 RemoteViewsFactory
的onDataSetChanged()
方法,这使你有机会获取任何新的数据。请注意,您可以在onDataSetChanged()
回调中同步执行处理密集型操作 。你保证这个调用将在元数据或者视图数据被提取之前完成RemoteViewsFactory
。另外,您可以在其中执行处理密集型操作getViewAt()
方法。如果该呼叫需要很长的时间,装载视图(由指定 RemoteViewsFactory
的 getLoadingView()
方法)将被显示在集合视图,直到它返回相应的位置。