Les widgets de collection sont spécialisés dans l'affichage de nombreux éléments du même type, tels que sous forme de collections de photos provenant d'une application de galerie, d'articles d'une application d'actualités ou messages d'une application de communication. Les widgets de collection se concentrent généralement sur cas: parcourir la collection et ouvrir un élément de celle-ci dans son vue détaillée. Les widgets des collections peuvent défiler verticalement.
Ces widgets utilisent
RemoteViewsService
pour afficher
collections reposant sur des données distantes, par exemple à partir d'un contenu
d'un fournisseur de services cloud. Ce widget présente les
des données à l'aide de l'un des types de vues suivants, appelés collecte
de vues:
ListView
- Vue qui affiche les éléments d'une liste déroulante verticale.
GridView
- Vue qui affiche les éléments d'une une grille de défilement bidimensionnelle.
StackView
- Carte empilée un peu comme un rolodex, dans lequel l'utilisateur peut retourner vers le haut ou vers le bas pour afficher respectivement la fiche précédente ou suivante.
AdapterViewFlipper
- Une
simple avec adaptateur
ViewAnimator
qui s'anime entre deux vues ou plus. Un seul enfant est affiché à la fois.
Comme ces vues de collection affichent des collections sauvegardées à partir de données distantes, elles
utiliser un Adapter
pour lier son utilisateur
à leurs données. Un élément Adapter
associe des éléments individuels à partir d'un ensemble de données.
à des objets View
individuels.
Comme ces vues de collection reposent sur des adaptateurs, le framework Android
doivent inclure une architecture supplémentaire pour permettre leur utilisation dans les widgets. Dans le contexte
d'un widget, Adapter
est remplacé par un
RemoteViewsFactory
,
qui est un wrapper léger autour de l'interface Adapter
. Pour une demande de
un élément spécifique de la collection, RemoteViewsFactory
crée et renvoie
l'élément de la collection
objet RemoteViews
. Pour inclure un
dans votre widget, implémentez RemoteViewsService
et
RemoteViewsFactory
RemoteViewsService
est un service qui permet à un adaptateur distant de demander
Objets RemoteViews
. RemoteViewsFactory
est une interface pour un adaptateur.
entre une vue de collection, telle que ListView
, GridView
et
StackView
, ainsi que les données sous-jacentes de cette vue. Source : StackWidget
exemple,
Voici un exemple de code récurrent permettant d'implémenter ce service
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. }
Exemple d'application
Les extraits de code de cette section sont également issus du fichier StackWidget
exemple:
Cet exemple est constitué d'une pile de 10 vues qui affichent les valeurs zéro à neuf. L'exemple de widget présente les comportements principaux suivants:
L'utilisateur peut faire pivoter verticalement la vue supérieure du widget pour afficher la suivante ou la vue précédente. Il s'agit d'un comportement
StackView
intégré.Sans interaction de l'utilisateur, le widget avance automatiquement dans sa de manière séquentielle, à la manière d'un diaporama. Cela est dû au paramètre
android:autoAdvanceViewId="@id/stack_view"
dans lares/xml/stackwidgetinfo.xml
. Ce paramètre s'applique à l'ID de la vue, qui, dans ce cas, correspond à l'ID de la vue de la pile.Si l'utilisateur touche la vue de dessus, le widget affiche la
Toast
message "Mode tactile n," où n est l'index (position) de la vue touchée. Pour en savoir plus sur la façon dont pour implémenter des comportements, consultez la section Ajouter un comportement items.
Implémenter des widgets avec des collections
Pour implémenter un widget avec des collections, suivez la procédure permettant d'implémenter les
widget, puis effectuez quelques étapes supplémentaires:
le fichier manifeste, ajouter une vue de collection à la mise en page du widget et modifier votre
AppWidgetProvider
.
Fichier manifeste pour les widgets avec des collections
Au-delà des exigences figurant dans la section Déclarer un widget dans le
un fichier manifeste, vous devez permettre aux widgets
collections à lier à votre RemoteViewsService
. Pour ce faire, déclarez
dans votre fichier manifeste avec l'autorisation
BIND_REMOTEVIEWS
Cela empêche les autres applications d'accéder librement aux données de votre widget.
Par exemple, lorsque vous créez un widget qui utilise RemoteViewsService
pour renseigner une
l'entrée du fichier manifeste peut se présenter comme suit:
<service android:name="MyWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
Dans cet exemple, android:name="MyWidgetService"
fait référence à votre sous-classe de
RemoteViewsService
Mise en page pour les widgets avec des collections
La principale condition requise pour votre fichier XML de mise en page de widget est qu'il inclue l'un des
les vues de la collection: ListView
, GridView
, StackView
ou
AdapterViewFlipper
Voici le fichier widget_layout.xml
du
StackWidget
exemple:
<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>
Notez que les vues vides doivent être frères de la vue de collection pour laquelle la Une vue vide représente un état vide.
En plus du fichier de mise en page pour l'ensemble de votre widget, créez une autre mise en page
qui définit la mise en page de chaque élément de la collection (par exemple,
une mise en page pour chaque
livre d'une collection de livres. L'exemple StackWidget
comporte
un seul fichier de mise en page d'élément, widget_item.xml
, car tous les éléments utilisent le même
mise en page.
Classe AppWidgetProvider pour les widgets avec des collections
Comme pour les widgets classiques, l'essentiel du code
Sous-classe AppWidgetProvider
va généralement de
onUpdate()
La principale différence dans votre implémentation pour onUpdate()
lorsque vous créez un
avec les collections est que vous devez appeler
setRemoteAdapter()
Cela indique à la vue de la collection où obtenir ses données.
Le RemoteViewsService
peut ensuite renvoyer votre implémentation de
RemoteViewsFactory
, et le widget peut diffuser les données appropriées. Lorsque vous
appelez cette méthode, transmettez un intent qui pointe vers votre implémentation de
RemoteViewsService
et l'ID du widget qui spécifie le widget à mettre à jour.
Par exemple, voici comment l'exemple StackWidget
implémente onUpdate()
de rappel pour définir RemoteViewsService
comme adaptateur distant pour l'objet
Collection de widgets:
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); }
Conserver les données
Comme décrit sur cette page, la sous-classe RemoteViewsService
fournit
RemoteViewsFactory
utilisé pour remplir la vue de la collection distante.
Plus précisément, effectuez les étapes suivantes:
Sous-classe
RemoteViewsService
.RemoteViewsService
est le service via qu'un adaptateur distant peut demanderRemoteViews
.Dans votre sous-classe
RemoteViewsService
, incluez une classe qui implémente leRemoteViewsFactory
.RemoteViewsFactory
est une interface pour entre une vue de collection distante, telle queListView
,GridView
etStackView
, et les données sous-jacentes de cette vue. Votre est responsable de la création d'un objetRemoteViews
pour chaque dans le jeu de données. Cette interface est un wrapper léger pourAdapter
.
Vous ne pouvez pas compter sur une seule instance de votre service ou sur les données qu'il contient pour
persister. Ne stockez pas de données dans votre RemoteViewsService
, sauf si elle est statique. Si
vous souhaitez que les données de votre widget soient conservées, la meilleure approche consiste à utiliser
ContentProvider
dont les données
persiste au-delà du cycle de vie du processus. Par exemple, le widget d'une épicerie
stocker l'état de chaque article de la liste de courses dans un emplacement persistant, tel qu'un
base de données SQL.
Le contenu principal de l'implémentation de RemoteViewsService
est le suivant :
RemoteViewsFactory
, décrit dans la section suivante.
Interface RemoteViewsFactory
Votre classe personnalisée qui implémente l'interface RemoteViewsFactory
fournit
le widget avec les données des éléments de sa collection. Pour ce faire, il
combine le fichier de mise en page XML de votre élément de widget avec une source de données. Cette source de
les données peuvent être n'importe quoi,
d'une base de données à un simple tableau. Dans le StackWidget
la source de données est un tableau de WidgetItems
. RemoteViewsFactory
sert d'adaptateur pour coller les données dans la vue de collecte à distance.
Les deux méthodes les plus importantes à mettre en œuvre
La sous-classe RemoteViewsFactory
est
onCreate()
et
getViewAt()
Le système appelle onCreate()
lors de la création de votre fabrique pour la première fois.
C'est ici que vous configurez les connexions ou les curseurs vers votre source de données. Pour
exemple, l'exemple StackWidget
utilise onCreate()
pour initialiser un tableau de
Objets WidgetItem
. Lorsque votre widget est actif, le système accède à ces
objets en utilisant leur position d'index dans le tableau et affiche le texte qu'ils
contient.
Voici un extrait de RemoteViewsFactory
de l'exemple StackWidget
qui affiche des parties de la méthode onCreate()
:
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 + "!")); } ... } ...
La méthode RemoteViewsFactory
getViewAt()
renvoie un objet RemoteViews
.
correspondant aux données au position
spécifié dans l'ensemble de données. Voici
Voici un extrait de l'implémentation RemoteViewsFactory
de l'exemple StackWidget
:
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; }
Ajouter un comportement à des éléments individuels
Les sections précédentes expliquent comment associer vos données à votre collection de widgets. Toutefois, que faire si vous souhaitez ajouter un comportement dynamique à chaque élément de votre la vue de la collection ?
Comme décrit dans la section Gérer les événements avec onUpdate()
, vous utilisez normalement
setOnClickPendingIntent()
pour définir le comportement de clic d'un objet, par exemple
faire en sorte qu'un bouton lance un Activity
. Toutefois,
cette approche n'est pas autorisée pour les vues enfants d'un élément de collection individuel.
Par exemple, vous pouvez utiliser setOnClickPendingIntent()
pour configurer un bouton global.
dans le widget Gmail qui lance l'application, par exemple, mais pas sur le
des éléments
de liste individuels.
Pour ajouter un comportement de clic à des éléments individuels d'une collection, utilisez plutôt
setOnClickFillInIntent()
Cela implique de configurer un modèle d'intent en attente
la vue de votre collection, puis en définissant un intent de remplissage pour chaque élément
collection via votre RemoteViewsFactory
.
Cette section utilise l'exemple StackWidget
pour décrire comment ajouter un comportement à
des éléments individuels. Dans l'exemple StackWidget
, si l'utilisateur touche la vue de dessus,
le widget affiche le message Toast
"Touched view n,"
où n est le
l'index (position) de la vue tactile. Voici comment cela fonctionne :
Le
StackWidgetProvider
: unAppWidgetProvider
sous-classe : crée un intent en attente avec une action personnalisée appeléeTOAST_ACTION
Lorsque l'utilisateur touche une vue, l'intent se déclenche et diffuse
TOAST_ACTION
Cette diffusion est interceptée par la classe
StackWidgetProvider
onReceive()
, et le widget affiche le messageToast
pour la vue tactile. Les données sur les éléments de la collection sont fournies par leRemoteViewsFactory
viaRemoteViewsService
.
Configurer le modèle d'intent en attente
Le StackWidgetProvider
(un
AppWidgetProvider
)
configure un intent en attente. Les éléments individuels d'une collection ne peuvent pas
et ses propres intents en attente. Au lieu de cela, la collection dans son ensemble configure un intent en attente
et les éléments individuels définissent un intent de remplissage pour créer
article par article.
Cette classe reçoit également l'annonce envoyée lorsque l'utilisateur touche un
vue. Il traite cet événement dans sa méthode onReceive()
. Si l'intent
l'action est TOAST_ACTION
, le widget affiche un message Toast
pour l'action
vue.
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); } }
Définir l'intent de remplissage
Votre RemoteViewsFactory
doit définir un intent de remplissage pour chaque élément du
collection. Cela permet de distinguer chaque action effectuée par un clic
d'un élément donné. L'intent de remplissage est ensuite combiné avec
PendingIntent
pour déterminer
L'intent final qui est exécuté lorsque l'utilisateur appuie sur l'élément
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; } ... }
Maintenir les données de collection à jour
La figure 2 illustre le flux de mise à jour dans un widget utilisant des collections. Émissions
comment le code du widget interagit avec RemoteViewsFactory
et comment vous pouvez
déclencher les mises à jour:
Les widgets qui utilisent des collections peuvent fournir aux utilisateurs un contenu à jour. Pour
Par exemple, le widget Gmail offre aux utilisateurs un aperçu de leur boîte de réception. Pour que
déclenchez la vue RemoteViewsFactory
et la vue de collection pour extraire et
pour afficher de nouvelles données.
Pour ce faire, utilisez la
AppWidgetManager
pour appeler
notifyAppWidgetViewDataChanged()
Cet appel entraîne un rappel de l'objet RemoteViewsFactory
onDataSetChanged()
, qui vous permet d'extraire de nouvelles données.
Vous pouvez effectuer des opérations gourmandes en traitement de manière synchrone dans
Rappel onDataSetChanged()
. Vous avez la garantie que cet appel sera terminé
avant que les métadonnées ou les données de la vue ne soient récupérées à partir de RemoteViewsFactory
. Toi
peut également effectuer des opérations nécessitant beaucoup de traitement dans le getViewAt()
.
. Si cet appel prend beaucoup de temps, la vue de chargement, spécifiée par
Objets RemoteViewsFactory
getLoadingView()
s'affiche à l'emplacement correspondant dans la vue de la collection.
jusqu'à ce qu'il s'affiche de nouveau.
Utiliser RemoteCollectionItems pour transmettre directement une collection
Android 12 (niveau d'API 31) ajoute setRemoteAdapter(int viewId,
RemoteViews.RemoteCollectionItems
items)
.
, qui permet à votre application de transmettre directement une collection lors de l'insertion d'un
vue de la collection. Si vous configurez votre adaptateur avec cette méthode, vous n'avez pas besoin de
implémenter un RemoteViewsFactory
et vous n'avez pas besoin d'appeler
notifyAppWidgetViewDataChanged()
En plus de faciliter l'insertion de l'adaptateur, cette approche
élimine la latence liée à l'ajout de nouveaux éléments lorsque les utilisateurs font défiler la liste
révéler un nouvel élément. Nous vous recommandons cette approche pour configurer l'adaptateur
votre ensemble d'éléments de collection est relativement petit. Toutefois, par exemple,
L'approche n'est pas adaptée si votre collection contient de nombreux Bitmaps
transmis à setImageViewBitmap
.
Si la collection n'utilise pas un ensemble constant de mises en page, c'est-à-dire si certaines
ne sont présents que parfois. Utilisez setViewTypeCount
pour spécifier la
nombre maximal de mises en page uniques que la collection peut contenir. Cela permet au
l'adaptateur peut être réutilisé lors des mises à jour du widget de votre application.
Voici un exemple d'implémentation des collections RemoteViews
simplifiées.
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() );