Collection widgets specialize in displaying many elements of the same type, such as collections of pictures from a gallery app, articles from a news app, or messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection and opening an element of the collection to its detail view. Collection widgets can scroll vertically.
These widgets use the
RemoteViewsService
to display
collections that are backed by remote data, such as from a content
provider. The widget presents the
data using one of the following view types, which are known as collection
views:
ListView
- A view that shows items in a vertically scrolling list.
GridView
- A view that shows items in a two-dimensional scrolling grid.
StackView
- A stacked card view—kind of like a rolodex—where the user can flick the front card up or down to see the previous or next card, respectively.
AdapterViewFlipper
- An
adapter-backed simple
ViewAnimator
that animates between two or more views. Only one child is shown at a time.
Because these collection views display collections backed by remote data, they
use an Adapter
to bind their user
interface to their data. An Adapter
binds individual items from a set of data
to individual View
objects.
And because these collection views are backed by adapters, the Android framework
must include extra architecture to support their use in widgets. In the context
of a widget, the Adapter
is replaced by a
RemoteViewsFactory
,
which is 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. To include a
collection view in your widget, implement RemoteViewsService
and
RemoteViewsFactory
.
RemoteViewsService
is a service that lets a remote adapter request
RemoteViews
objects. RemoteViewsFactory
is an interface for an adapter
between a collection view—such as ListView
, GridView
, and
StackView
—and the underlying data for that view. From the StackWidget
sample,
here is an example of the boilerplate code to implement this service and
interface:
Kotlin
class StackWidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { return StackRemoteViewsFactory(this.applicationContext, intent) } } class StackRemoteViewsFactory( private val context: Context, intent: Intent ) : RemoteViewsService.RemoteViewsFactory { // See the RemoteViewsFactory API reference for the full list of methods to // implement. }
Java
public class StackWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new StackRemoteViewsFactory(this.getApplicationContext(), intent); } } class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { // See the RemoteViewsFactory API reference for the full list of methods to // implement. }
Sample application
The code excerpts in this section are also drawn from the StackWidget
sample:
This sample consists of a stack of ten views that display the values zero through nine. The sample widget has these primary behaviors:
The user can vertically fling the top view in the widget to display the next or previous view. This is a built-in
StackView
behavior.Without any user interaction, the widget automatically advances through its views in sequence, like a slideshow. 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 widget displays the
Toast
message "Touched view n," where n is the index (position) of the touched view. For more discussion on how to implement behaviors, see the Add behavior to individual items section.
Implement widgets with collections
To implement a widget with collections, follow the procedure to implement any
widget, followed by a few additional steps:
modify the manifest, add a collection view to the widget layout, and modify your
AppWidgetProvider
subclass.
Manifest for widgets with collections
Beyond the requirements listed in Declare a widget in the
manifest, you need to make it possible for widgets with
collections to bind to your RemoteViewsService
. Do this by declaring the
service in your manifest file with the permission
BIND_REMOTEVIEWS
.
This prevents other applications from freely accessing your widget's data.
For example, when creating a widget that uses RemoteViewsService
to populate a
collection view, the manifest entry might look like this:
<service android:name="MyWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
In this example, android:name="MyWidgetService"
refers to your subclass of
RemoteViewsService
.
Layout for widgets with collections
The main requirement for your widget layout XML file is that it include one of
the collection views: ListView
, GridView
, StackView
, or
AdapterViewFlipper
. Here is the widget_layout.xml
file for the
StackWidget
sample:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView
android:id="@+id/stack_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:loopViews="true" />
<TextView
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 widget, 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. The StackWidget
sample has
only one item layout file, widget_item.xml
, since all items use the same
layout.
AppWidgetProvider class for widgets with collections
As with regular widgets, the bulk of the code in your
AppWidgetProvider
subclass
typically goes in
onUpdate()
.
The major difference in your implementation for onUpdate()
when creating a
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, pass an intent that points to your implementation of
RemoteViewsService
and the widget ID that specifies the widget to update.
For example, here's how the StackWidget
sample implements the onUpdate()
callback method to set the RemoteViewsService
as the remote adapter for the
widget collection:
Kotlin
override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Update each of the widgets with the remote adapter. appWidgetIds.forEach { appWidgetId -> // Set up the intent that starts the StackViewService, which // provides the views for this collection. val intent = Intent(context, StackWidgetService::class.java).apply { // Add the widget ID to the intent extras. putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) } // Instantiate the RemoteViews object for the widget layout. val views = RemoteViews(context.packageName, R.layout.widget_layout).apply { // 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. setRemoteAdapter(R.id.stack_view, intent) // The empty view is displayed when the collection has no items. // It must be in the same layout used to instantiate the // RemoteViews object. setEmptyView(R.id.stack_view, R.id.empty_view) } // Do additional processing specific to this widget. appWidgetManager.updateAppWidget(appWidgetId, views) } super.onUpdate(context, appWidgetManager, appWidgetIds) }
Java
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // Update each of the widgets with the remote adapter. for (int i = 0; i < appWidgetIds.length; ++i) { // Set up the intent that starts the StackViewService, which // provides the views for this collection. Intent intent = new Intent(context, StackWidgetService.class); // Add the 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 widget layout. RemoteViews views = 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. views.setRemoteAdapter(R.id.stack_view, intent); // The empty view is displayed when the collection has no items. // It must be in the same layout used to instantiate the RemoteViews // object. views.setEmptyView(R.id.stack_view, R.id.empty_view); // Do additional processing specific to this widget. appWidgetManager.updateAppWidget(appWidgetIds[i], views); } super.onUpdate(context, appWidgetManager, appWidgetIds); }
Persist data
As described on this page, the RemoteViewsService
subclass provides
the RemoteViewsFactory
used to populate the remote collection view.
Specifically, 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
,StackView
—and the underlying data for that view. Your implementation is responsible for making aRemoteViews
object for each item in the dataset. This interface is a thin wrapper aroundAdapter
.
You can’t rely on a single instance of your service, or any data it contains, to
persist. Don't store data in your RemoteViewsService
unless it is static. If
you want your widget’s data to persist, the best approach is to use a
ContentProvider
whose data
persists beyond the process lifecycle. For example, a grocery store widget can
store the state of each grocery list item in a persistent location, such as a
SQL database.
The primary contents of the RemoteViewsService
implementation is its
RemoteViewsFactory
, described in the following section.
RemoteViewsFactory interface
Your custom class that implements the RemoteViewsFactory
interface provides
the widget with the data for the items in its collection. To do this, it
combines your widget item XML layout file with a source of data. This source of
data can be anything from a database to a simple array. In the StackWidget
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 or cursors to your data source. For
example, the StackWidget
sample uses onCreate()
to initialize an array of
WidgetItem
objects. When your widget is active, the system accesses these
objects using their index position in the array and displays the text they
contain.
Here is an excerpt from the StackWidget
sample's RemoteViewsFactory
implementation that shows portions of the onCreate()
method:
Kotlin
private const val REMOTE_VIEW_COUNT: Int = 10 class StackRemoteViewsFactory( private val context: Context ) : RemoteViewsService.RemoteViewsFactory { private lateinit var widgetItems: List<WidgetItem> override fun onCreate() { // In onCreate(), set up any connections or cursors to your data // source. Heavy lifting, such as downloading or creating content, // must be deferred to onDataSetChanged() or getViewAt(). Taking // more than 20 seconds on this call results in an ANR. widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") } ... } ... }
Java
class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static final int REMOTE_VIEW_COUNT = 10; private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>(); public void onCreate() { // In onCreate(), setup any connections or cursors to your data // source. Heavy lifting, such as downloading or creating content, // must be deferred to onDataSetChanged() or getViewAt(). Taking // more than 20 seconds on this call results in an ANR. for (int i = 0; i < REMOTE_VIEW_COUNT; i++) { widgetItems.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 StackWidget
sample's RemoteViewsFactory
implementation:
Kotlin
override fun getViewAt(position: Int): RemoteViews { // Construct a remote views item based on the widget item XML file // and set the text based on the position. return RemoteViews(context.packageName, R.layout.widget_item).apply { setTextViewText(R.id.widget_item, widgetItems[position].text) } }
Java
public RemoteViews getViewAt(int position) { // Construct a remote views item based on the widget item XML file // and set the text based on the position. RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_item); views.setTextViewText(R.id.widget_item, widgetItems.get(position).text); return views; }
Add behavior to individual items
The preceding sections show how to bind your data to your widget collection. But what if you want to add dynamic behavior to the individual items in your collection view?
As described in Handle events with the onUpdate()
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.
For example, you can use setOnClickPendingIntent()
to set up a global button
in the Gmail 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, 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 StackWidget
sample to describe how to add behavior to
individual items. In the StackWidget
sample, if the user touches the top view,
the 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 with a custom action calledTOAST_ACTION
.When the user touches a view, the intent fires and it broadcasts
TOAST_ACTION
.This broadcast is intercepted by the
StackWidgetProvider
class'sonReceive()
method, and the widget displays theToast
message for the touched view. The data for the collection items is provided by theRemoteViewsFactory
through theRemoteViewsService
.
Set up the pending intent template
The StackWidgetProvider
(an
AppWidgetProvider
subclass)
sets up a pending intent. Individual items of a collection can't 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 widget displays a Toast
message for the current
view.
Kotlin
const val TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION" const val EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM" class StackWidgetProvider : AppWidgetProvider() { ... // Called when the BroadcastReceiver receives an Intent broadcast. // Checks whether the intent's action is TOAST_ACTION. If it is, the // widget displays a Toast message for the current item. override fun onReceive(context: Context, intent: Intent) { val mgr: AppWidgetManager = AppWidgetManager.getInstance(context) if (intent.action == TOAST_ACTION) { val appWidgetId: Int = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) // EXTRA_ITEM represents a custom value provided by the Intent // passed to the setOnClickFillInIntent() method to indicate the // position of the clicked item. See StackRemoteViewsFactory in // Set the fill-in Intent for details. val viewIndex: Int = intent.getIntExtra(EXTRA_ITEM, 0) Toast.makeText(context, "Touched view $viewIndex", Toast.LENGTH_SHORT).show() } super.onReceive(context, intent) } override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Update each of the widgets with the remote adapter. appWidgetIds.forEach { appWidgetId -> // Sets up the intent that points to the StackViewService that // provides the views for this collection. val intent = Intent(context, StackWidgetService::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) // When intents are compared, the extras are ignored, so embed // the extra sinto the data so that the extras are not ignored. data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) } val rv = RemoteViews(context.packageName, R.layout.widget_layout).apply { setRemoteAdapter(R.id.stack_view, intent) // The empty view is displayed when the collection has no items. // It must be a sibling of the collection view. 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 can't 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. val toastPendingIntent: PendingIntent = Intent( context, StackWidgetProvider::class.java ).run { // Set the action for the intent. // When the user touches a particular view, it has the effect of // broadcasting TOAST_ACTION. action = TOAST_ACTION putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT) } rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent) appWidgetManager.updateAppWidget(appWidgetId, rv) } super.onUpdate(context, appWidgetManager, appWidgetIds) } }
Java
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 whether the intent's action is TOAST_ACTION. If it is, the // 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); // EXTRA_ITEM represents a custom value provided by the Intent // passed to the setOnClickFillInIntent() method to indicate the // position of the clicked item. See StackRemoteViewsFactory in // Set the fill-in Intent for details. 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 widgets with the remote adapter. for (int i = 0; i < appWidgetIds.length; ++i) { // Sets up the intent that points to the StackViewService that // provides 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 embed // the extras into the data so that the extras are not // 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 // must 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 can't 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 has 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); } }
Set 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 to determine
the final intent that is executed when the item is tapped.
Kotlin
private const val REMOTE_VIEW_COUNT: Int = 10 class StackRemoteViewsFactory( private val context: Context, intent: Intent ) : RemoteViewsService.RemoteViewsFactory { private lateinit var widgetItems: List<WidgetItem> private val appWidgetId: Int = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) override fun onCreate() { // In onCreate(), set up any connections or cursors to your data source. // Heavy lifting, such as downloading or creating content, must be // deferred to onDataSetChanged() or getViewAt(). Taking more than 20 // seconds on this call results in an ANR. widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") } ... } ... override fun getViewAt(position: Int): RemoteViews { // Construct a remote views item based on the widget item XML file // and set the text based on the position. return RemoteViews(context.packageName, R.layout.widget_item).apply { setTextViewText(R.id.widget_item, widgetItems[position].text) // Set a fill-intent to fill in the pending intent template. // that is set on the collection view in StackWidgetProvider. val fillInIntent = Intent().apply { Bundle().also { extras -> extras.putInt(EXTRA_ITEM, position) putExtras(extras) } } // Make it possible to distinguish the individual on-click // action of a given item. setOnClickFillInIntent(R.id.widget_item, fillInIntent) ... } } ... }
Java
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 count = 10; private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>(); private Context context; private int appWidgetId; public StackRemoteViewsFactory(Context context, Intent intent) { this.context = context; appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } // Initialize the data set. public void onCreate() { // In onCreate(), set up any connections or cursors to your data // source. Heavy lifting, such as downloading or creating // content, must be deferred to onDataSetChanged() or // getViewAt(). Taking more than 20 seconds on this call results // in an ANR. for (int i = 0; i < count; i++) { widgetItems.add(new WidgetItem(i + "!")); } ... } // Given the position (index) of a WidgetItem in the array, use the // item's text value in combination with the widget item XML file to // construct a RemoteViews object. public RemoteViews getViewAt(int position) { // Position always ranges from 0 to getCount() - 1. // Construct a RemoteViews item based on the widget item XML // file and set the text based on the position. RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_item); rv.setTextViewText(R.id.widget_item, widgetItems.get(position).text); // Set a fill-intent 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; } ... }
Keep collection data fresh
Figure 2 illustrates the update flow in a widget that uses collections. It shows
how the widget code interacts with the RemoteViewsFactory
and how you can
trigger updates:
Widgets that use collections can provide users with up-to-date content. For
example, the Gmail widget gives users a snapshot of their inbox. To make this
possible, trigger your RemoteViewsFactory
and collection view to fetch and
display new data.
To do this, use the
AppWidgetManager
to call
notifyAppWidgetViewDataChanged()
. This call results in a callback to your RemoteViewsFactory
object’s
onDataSetChanged()
method, which lets you fetch any new data.
You can perform processing-intensive operations synchronously within the
onDataSetChanged()
callback. You are guaranteed that this call is completed
before the metadata or view data is fetched from the RemoteViewsFactory
. You
can also perform processing-intensive operations within the getViewAt()
method. If this call takes long, the loading view—specified by the
RemoteViewsFactory
object's
getLoadingView()
method—is displayed in the corresponding position of the collection view
until it returns.
Use RemoteCollectionItems to pass along a collection directly
Android 12 (API level 31) adds the setRemoteAdapter(int viewId,
RemoteViews.RemoteCollectionItems
items)
method, which lets your app pass along a collection directly when populating a
collection view. If you set your adapter using this method, you don't need to
implement a RemoteViewsFactory
and you don't need to call
notifyAppWidgetViewDataChanged()
.
In addition to making it easier to populate your adapter, this approach also
removes the latency for populating new itemswhen users scroll down the list to
reveal a new item. This approach to setting the adapter is preferred as long as
your set of collection items is relatively small. However, for example, this
approach doesn't work well if your collection contains numerous Bitmaps
being
passed to setImageViewBitmap
.
If the collection doesn’t use a constant set of layouts—that is, if some
items are only sometimes present—use setViewTypeCount
to specify the
maximum number of unique layouts that the collection can contain. This lets the
adapter be reused across updates to your app widget.
Here’s an example of how to implement simplified RemoteViews
collections.
Kotlin
val itemLayouts = listOf( R.layout.item_type_1, R.layout.item_type_2, ... ) remoteView.setRemoteAdapter( R.id.list_view, RemoteViews.RemoteCollectionItems.Builder() .addItem(/* id= */ ID_1, RemoteViews(context.packageName, R.layout.item_type_1)) .addItem(/* id= */ ID_2, RemoteViews(context.packageName, R.layout.item_type_2)) ... .setViewTypeCount(itemLayouts.count()) .build() )
Java
List<Integer> itemLayouts = Arrays.asList( R.layout.item_type_1, R.layout.item_type_2, ... ); remoteView.setRemoteAdapter( R.id.list_view, new RemoteViews.RemoteCollectionItems.Builder() .addItem(/* id= */ ID_1, new RemoteViews(context.getPackageName(), R.layout.item_type_1)) .addItem(/* id= */ ID_2, new RemoteViews(context.getPackageName(), R.layout.item_type_2)) ... .setViewTypeCount(itemLayouts.size()) .build() );