Sammlungswidgets sind darauf spezialisiert, viele Elemente desselben Typs anzuzeigen, z. B. Sammlungen von Bildern aus einer Galerie-App, Artikel aus einer Nachrichten-App oder Nachrichten aus einer Kommunikations-App. Sammlungswidgets konzentrieren sich in der Regel auf zwei Anwendungsfälle: das Durchsuchen der Sammlung und das Öffnen eines Elements der Sammlung in der Detailansicht. Sammlungs-Widgets können vertikal gescrollt werden.
Diese Widgets verwenden RemoteViewsService, um Sammlungen anzuzeigen, die auf Remote-Daten basieren, z. B. von einem Contentanbieter. Das Widget stellt die Daten mit einem der folgenden Ansichtstypen dar, die als Sammlungsansichten bezeichnet werden:
ListView: Eine Ansicht, in der Elemente in einer vertikal scrollbaren Liste angezeigt werden.
GridView: Eine Ansicht, in der Elemente in einem zweidimensionalen scrollbaren Raster angezeigt werden.
StackView: Eine gestapelte Kartenansicht, ähnlich wie ein Rolodex, bei der der Nutzer die vorderste Karte nach oben oder unten wischen kann, um die vorherige oder nächste Karte zu sehen.
AdapterViewFlipper: Ein adaptergestütztes ViewAnimator, das zwischen zwei oder mehr Ansichten animiert wird. Es wird jeweils nur ein Kind angezeigt.
Da in diesen Sammlungsansichten Sammlungen angezeigt werden, die auf Remote-Daten basieren, wird ein Adapter verwendet, um die Benutzeroberfläche an die Daten zu binden. Ein Adapter bindet einzelne Elemente aus einem Datensatz an einzelne View-Objekte.
Da diese Sammlungsansichten von Adaptern unterstützt werden, muss das Android-Framework zusätzliche Architektur enthalten, um ihre Verwendung in Widgets zu unterstützen. Im Kontext eines Widgets wird Adapter durch ein RemoteViewsFactory ersetzt, das ein dünner Wrapper um die Adapter-Schnittstelle ist. Wenn ein bestimmtes Element in der Sammlung angefordert wird, erstellt und gibt die RemoteViewsFactory das Element für die Sammlung als RemoteViews-Objekt zurück. Wenn Sie eine Sammlungsansicht in Ihr Widget einfügen möchten, implementieren Sie RemoteViewsService und RemoteViewsFactory.
RemoteViewsService ist ein Dienst, mit dem ein Remote-Adapter RemoteViews-Objekte anfordern kann. RemoteViewsFactory ist eine Schnittstelle für einen Adapter zwischen einer Sammlungsansicht, z. B. ListView, GridView und StackView, und den zugrunde liegenden Daten für diese Ansicht. Hier ist ein Beispiel für den Boilerplate-Code aus dem StackWidget-Beispiel, mit dem dieser Dienst und diese Schnittstelle implementiert werden:
class StackWidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory = 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. }
Beispielanwendung
Die Codeauszüge in diesem Abschnitt stammen ebenfalls aus dem Beispiel StackWidget:
StackWidget.Dieses Beispiel besteht aus einem Stapel von zehn Ansichten, in denen die Werte null bis neun angezeigt werden. Das Beispiel-Widget hat die folgenden primären Verhaltensweisen:
Der Nutzer kann die oberste Ansicht im Widget vertikal verschieben, um die nächste oder vorherige Ansicht anzuzeigen. Dies ist ein integriertes
StackView-Verhalten.Ohne Nutzerinteraktion wechselt das Widget automatisch nacheinander durch die Ansichten, wie bei einer Diashow. Das liegt an der Einstellung
android:autoAdvanceViewId="@id/stack_view"in der Dateires/xml/stackwidgetinfo.xml. Diese Einstellung gilt für die Ansichts-ID, in diesem Fall die Ansichts-ID der Stack-Ansicht.Wenn der Nutzer die oberste Ansicht berührt, wird im Widget die
Toast-Nachricht „Ansicht n berührt“ angezeigt, wobei n der Index (die Position) der berührten Ansicht ist. Weitere Informationen zum Implementieren von Verhaltensweisen finden Sie im Abschnitt Verhalten zu einzelnen Elementen hinzufügen.
Widgets mit Sammlungen implementieren
Wenn Sie ein Widget mit Sammlungen implementieren möchten, folgen Sie der Anleitung zum Implementieren eines Widgets und führen Sie dann die folgenden zusätzlichen Schritte aus: Ändern Sie das Manifest, fügen Sie dem Widget-Layout eine Sammlungsansicht hinzu und ändern Sie Ihre AppWidgetProvider-Unterklasse.
Manifest für Widgets mit Sammlungen
Zusätzlich zu den Anforderungen, die unter Widget im Manifest deklarieren aufgeführt sind, müssen Widgets mit Sammlungen an Ihre RemoteViewsService gebunden werden können. Deklarieren Sie dazu den Dienst in Ihrer Manifestdatei mit der Berechtigung BIND_REMOTEVIEWS.
Dadurch wird verhindert, dass andere Anwendungen frei auf die Daten Ihres Widgets zugreifen können.
Wenn du beispielsweise ein Widget erstellst, das RemoteViewsService zum Ausfüllen einer Sammlungsansicht verwendet, könnte der Manifesteintrag so aussehen:
<service android:name="MyWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
In diesem Beispiel bezieht sich android:name="MyWidgetService" auf Ihre abgeleitete Klasse von RemoteViewsService.
Layout für Widgets mit Sammlungen
Die wichtigste Anforderung an die XML-Datei für das Widget-Layout ist, dass sie eine der Sammlungsansichten enthält: ListView, GridView, StackView oder AdapterViewFlipper. Hier sehen Sie die widget_layout.xml-Datei für das StackWidget-Beispiel:
<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>
Beachten Sie, dass leere Ansichten gleichrangig mit der Sammlungsansicht sein müssen, für die die leere Ansicht den leeren Zustand darstellt.
Erstelle zusätzlich zur Layoutdatei für das gesamte Widget eine weitere Layoutdatei, die das Layout für jedes Element in der Sammlung definiert, z. B. ein Layout für jedes Buch in einer Büchersammlung. Das Beispiel StackWidget hat nur eine Elementlayoutdatei, widget_item.xml, da alle Elemente dasselbe Layout verwenden.
AppWidgetProvider-Klasse für Widgets mit Sammlungen
Wie bei normalen Widgets befindet sich der Großteil des Codes in der AppWidgetProvider-Unterklasse normalerweise in onUpdate().
Der Hauptunterschied bei der Implementierung für onUpdate() beim Erstellen eines Widgets mit Sammlungen besteht darin, dass Sie setRemoteAdapter() aufrufen müssen.
So weiß die Sammlungsansicht, woher sie ihre Daten beziehen soll.
Der RemoteViewsService kann dann Ihre Implementierung von RemoteViewsFactory zurückgeben und das Widget kann die entsprechenden Daten bereitstellen. Wenn Sie diese Methode aufrufen, übergeben Sie einen Intent, der auf Ihre Implementierung von RemoteViewsService verweist, und die Widget-ID, die das zu aktualisierende Widget angibt.
Im Beispiel StackWidget wird die Callback-Methode onUpdate() so implementiert, dass RemoteViewsService als Remote-Adapter für die Widget-Sammlung festgelegt wird:
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) }
Daten persistent speichern
Wie auf dieser Seite beschrieben, stellt die Unterklasse RemoteViewsService die RemoteViewsFactory bereit, die zum Ausfüllen der Remote-Sammlungsansicht verwendet wird.
Führen Sie dazu die folgenden Schritte aus:
Unterklasse
RemoteViewsService.RemoteViewsServiceist der Dienst, über den ein Remote-AdapterRemoteViewsanfordern kann.Fügen Sie in der Unterklasse
RemoteViewsServiceeine Klasse ein, die die SchnittstelleRemoteViewsFactoryimplementiert.RemoteViewsFactoryist eine Schnittstelle für einen Adapter zwischen einer Remote-Sammlungsansicht, z. B.ListView,GridView,StackView, und den zugrunde liegenden Daten für diese Ansicht. Ihre Implementierung ist dafür verantwortlich, für jedes Element im Dataset einRemoteViews-Objekt zu erstellen. Diese Schnittstelle ist ein Thin-Wrapper umAdapter.
Sie können sich nicht darauf verlassen, dass eine einzelne Instanz Ihres Dienstes oder die darin enthaltenen Daten dauerhaft bestehen bleiben. Speichern Sie keine Daten in RemoteViewsService, es sei denn, sie sind statisch. Wenn die Daten Ihres Widgets beibehalten werden sollen, verwenden Sie am besten einen ContentProvider, dessen Daten über den Lebenszyklus des Prozesses hinaus beibehalten werden. Ein Widget für ein Lebensmittelgeschäft kann beispielsweise den Status jedes Artikels auf der Einkaufsliste an einem nichtflüchtigen Speicherort wie einer SQL-Datenbank speichern.
Der Hauptinhalt der RemoteViewsService-Implementierung ist die RemoteViewsFactory, die im folgenden Abschnitt beschrieben wird.
RemoteViewsFactory-Schnittstelle
Ihre benutzerdefinierte Klasse, die die RemoteViewsFactory-Schnittstelle implementiert, stellt dem Widget die Daten für die Elemente in seiner Sammlung bereit. Dazu wird die XML-Layoutdatei für Widget-Elemente mit einer Datenquelle kombiniert. Diese Datenquelle kann alles sein, von einer Datenbank bis hin zu einem Array. Im Beispiel StackWidget ist die Datenquelle ein Array von WidgetItems. RemoteViewsFactory fungiert als Adapter, um die Daten mit der Remote-Sammlungsansicht zu verknüpfen.
Die beiden wichtigsten Methoden, die Sie für Ihre RemoteViewsFactory-Unterklasse implementieren müssen, sind onCreate() und getViewAt().
Das System ruft onCreate() auf, wenn die Factory zum ersten Mal erstellt wird.
Hier richten Sie alle Verbindungen oder Cursor zu Ihrer Datenquelle ein. Im Beispiel StackWidget wird beispielsweise onCreate() verwendet, um ein Array von WidgetItem-Objekten zu initialisieren. Wenn das Widget aktiv ist, greift das System über die Indexposition im Array auf diese Objekte zu und zeigt den darin enthaltenen Text an.
Hier ist ein Auszug aus der RemoteViewsFactory-Implementierung des StackWidget-Beispiels, der Teile der onCreate()-Methode zeigt:
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!") } } }
Die Methode RemoteViewsFactory getViewAt() gibt ein RemoteViews-Objekt zurück, das den Daten am angegebenen position im Dataset entspricht. Hier ein Auszug aus der RemoteViewsFactory-Implementierung des StackWidget-Beispiels:
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) } }
Verhalten für einzelne Elemente hinzufügen
In den vorherigen Abschnitten wurde beschrieben, wie Sie Ihre Daten an Ihre Widget-Sammlung binden. Was aber, wenn Sie den einzelnen Elementen in Ihrer Sammlungsansicht dynamisches Verhalten hinzufügen möchten?
Wie unter Ereignisse mit der Klasse onUpdate() verarbeiten beschrieben, verwenden Sie normalerweise setOnClickPendingIntent(), um das Klickverhalten eines Objekts festzulegen, z. B. um eine Schaltfläche zum Starten eines Activity zu veranlassen. Dieser Ansatz ist jedoch für untergeordnete Ansichten in einem einzelnen Sammlungselement nicht zulässig.
Sie können beispielsweise setOnClickPendingIntent() verwenden, um im Gmail-Widget eine globale Schaltfläche einzurichten, über die die App gestartet wird, aber nicht für die einzelnen Listenelemente.
Verwenden Sie stattdessen setOnClickFillInIntent(), um einzelnen Elementen in einer Sammlung ein Klickverhalten hinzuzufügen. Richte eine Vorlage für ausstehende Intents für deine Sammlungsansicht ein und lege für jedes Element in der Sammlung einen Intent zum Ausfüllen mit deinem RemoteViewsFactory fest.
In diesem Abschnitt wird anhand des Beispiels StackWidget beschrieben, wie Sie einzelnen Elementen Verhalten hinzufügen. Wenn der Nutzer im Beispiel StackWidget die oberste Ansicht berührt, wird im Widget die Toast-Nachricht „Touched view n“ angezeigt, wobei n der Index (die Position) der berührten Ansicht ist. Das geht so:
Die
StackWidgetProvider, eine Unterklasse vonAppWidgetProvider, erstellt einen ausstehenden Intent mit einer benutzerdefinierten Aktion namensTOAST_ACTION.Wenn der Nutzer eine Ansicht berührt, wird der Intent ausgelöst und
TOAST_ACTIONübertragen.Diese Übertragung wird von der Methode
onReceive()der KlasseStackWidgetProviderabgefangen und das Widget zeigt die NachrichtToastfür die berührte Ansicht an. Die Daten für die Sammlungselemente werden vomRemoteViewsFactoryüber denRemoteViewsServicebereitgestellt.
Vorlage für ausstehende Intents einrichten
Die StackWidgetProvider (eine abgeleitete Klasse von AppWidgetProvider) richtet einen Pending Intent ein. Für einzelne Elemente einer Sammlung können keine eigenen Pending Intents eingerichtet werden. Stattdessen wird für die gesamte Sammlung eine PendingIntent-Vorlage eingerichtet und für die einzelnen Elemente ein FillInIntent, um für jedes Element ein individuelles Verhalten zu erstellen.
Diese Klasse empfängt auch die Übertragung, die gesendet wird, wenn der Nutzer eine Ansicht berührt. Dieses Ereignis wird in der Methode onReceive() verarbeitet. Wenn die Aktion des Intents TOAST_ACTION ist, zeigt das Widget eine Toast-Nachricht für die aktuelle Ansicht an.
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)) // The template must be mutable, because each item fills in its // own extras through setOnClickFillInIntent(). PendingIntent.getBroadcast( context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE ) } rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent) appWidgetManager.updateAppWidget(appWidgetId, rv) } super.onUpdate(context, appWidgetManager, appWidgetIds) } }
Fill-In-Intent festlegen
In der RemoteViewsFactory muss für jedes Element in der Sammlung eine Intent-Anweisung zum Ausfüllen festgelegt werden. So lässt sich die jeweilige On-Click-Aktion eines bestimmten Elements unterscheiden. Der Fill-In-Intent wird dann mit der Vorlage PendingIntent kombiniert, um den endgültigen Intent zu bestimmen, der ausgeführt wird, wenn auf das Element getippt wird.
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) // ... } } // ... }
Sammlungsdaten auf dem neuesten Stand halten
Abbildung 2 zeigt den Aktualisierungsablauf in einem Widget, das Sammlungen verwendet. Es zeigt, wie der Widget-Code mit dem RemoteViewsFactory interagiert und wie Sie Aktualisierungen auslösen können:
RemoteViewsFactory während Updates.Widgets, die Sammlungen verwenden, können Nutzern aktuelle Inhalte bieten. Das Gmail-Widget bietet Nutzern beispielsweise einen Überblick über ihren Posteingang. Dazu müssen Sie die RemoteViewsFactory und die Sammlungsansicht auslösen, um neue Daten abzurufen und anzuzeigen.
Verwenden Sie dazu AppWidgetManager, um notifyAppWidgetViewDataChanged() aufzurufen. Dieser Aufruf führt zu einem Callback an die Methode onDataSetChanged() Ihres RemoteViewsFactory-Objekts, mit der Sie neue Daten abrufen können.
Sie können rechenintensive Vorgänge synchron im onDataSetChanged()-Callback ausführen. Dieser Aufruf wird abgeschlossen, bevor die Metadaten oder Ansichtsdaten aus dem RemoteViewsFactory abgerufen werden. Sie können auch rechenintensive Vorgänge innerhalb der getViewAt()-Methode ausführen. Wenn dieser Aufruf lange dauert, wird die Ladeansicht, die durch die Methode getLoadingView() des RemoteViewsFactory-Objekts angegeben wird, an der entsprechenden Position der Sammlungsansicht angezeigt, bis der Aufruf zurückgegeben wird.
„RemoteCollectionItems“ verwenden, um eine Sammlung direkt zu übergeben
In Android 12 (API-Level 31) wird die Methode setRemoteAdapter(int viewId,
RemoteViews.RemoteCollectionItems
items) hinzugefügt, mit der Ihre App eine Sammlung direkt beim Ausfüllen einer Sammlungsansicht weitergeben kann. Wenn Sie Ihren Adapter mit dieser Methode festlegen, müssen Sie keinen RemoteViewsFactory implementieren und notifyAppWidgetViewDataChanged() nicht aufrufen.
Dieser Ansatz erleichtert nicht nur das Befüllen des Adapters, sondern beseitigt auch die Latenz beim Befüllen neuer Elemente, wenn Nutzer durch die Liste scrollen, um ein neues Element anzuzeigen. Diese Methode zum Festlegen des Adapters ist zu bevorzugen, solange die Anzahl der Sammlungselemente relativ klein ist. Dieser Ansatz funktioniert jedoch beispielsweise nicht gut, wenn Ihre Sammlung zahlreiche Bitmaps enthält, die an setImageViewBitmap übergeben werden.
Wenn die Sammlung keine konstante Gruppe von Layouts verwendet, d. h., wenn einige Elemente nur manchmal vorhanden sind, geben Sie mit setViewTypeCount die maximale Anzahl eindeutiger Layouts an, die die Sammlung enthalten kann. So kann der Adapter bei Aktualisierungen des App-Widgets wiederverwendet werden.
Hier ein Beispiel für die Implementierung vereinfachter RemoteViews-Sammlungen.
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() )