Los widgets de colección se especializan en mostrar muchos elementos del mismo tipo, como como colecciones de imágenes de una app de galería, artículos de una app de noticias mensajes desde una app de comunicación. Los widgets de colección suelen enfocarse en dos usos casos: navegar por la colección y abrir un elemento de esta a su vista detallada. Los widgets de colección se pueden desplazar de forma vertical.
Estos widgets usan
RemoteViewsService
para mostrar
colecciones respaldadas por datos remotos, por ejemplo, de un contenido
proveedor. El widget presenta la
usando uno de los siguientes tipos de vista, que se conocen como recopilación
vistas:
ListView
- Una vista que muestra elementos de una Lista de desplazamiento vertical
GridView
- Una vista que muestra elementos de una cuadrícula de desplazamiento bidimensional.
StackView
- Una tarjeta apilada como un fichero, en el que el usuario puede girarla arriba o abajo para ver la tarjeta anterior o siguiente, respectivamente.
AdapterViewFlipper
- Un
simple respaldado por un adaptador
ViewAnimator
con animación entre dos o más vistas. Solo se muestra un elemento secundario a la vez.
Como estas vistas de colecciones muestran colecciones respaldadas por datos remotos,
usar un Adapter
para vincular a su usuario
interfaz de usuario con sus datos. Una Adapter
vincula elementos individuales de un conjunto de datos
a objetos View
individuales.
Debido a que estas vistas de colección están respaldadas por adaptadores, el framework de Android
debe incluir arquitectura adicional para respaldar su uso en widgets. En el contexto
de un widget, el Adapter
se reemplaza por un
RemoteViewsFactory
,
que es un wrapper delgado alrededor de la interfaz Adapter
. Cuando se solicite un
elemento específico de la colección, RemoteViewsFactory
crea y muestra
el elemento de la colección como
objeto RemoteViews
. Para incluir un
de colecciones en el widget, implementa RemoteViewsService
y
RemoteViewsFactory
RemoteViewsService
es un servicio que permite solicitar un adaptador remoto.
RemoteViews
. RemoteViewsFactory
es una interfaz para un adaptador.
entre una vista de colección, como ListView
, GridView
y
StackView
, y los datos subyacentes para esa vista. De la StackWidget
muestra,
Este es un ejemplo del código estándar para implementar este servicio.
:
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. }
App de muestra
Los extractos de código de esta sección también se extrajeron de la StackWidget
muestra:
Este ejemplo consta de una pila de diez vistas que muestran los valores cero hasta nueve. El widget de muestra tiene los siguientes comportamientos principales:
El usuario puede desplazar verticalmente la vista superior del widget para mostrar la siguiente o anterior. Este es un comportamiento
StackView
integrado.Sin interacción del usuario, el widget avanza automáticamente por su vistas en secuencia, como una presentación de diapositivas. Esto se debe a la configuración
android:autoAdvanceViewId="@id/stack_view"
en archivores/xml/stackwidgetinfo.xml
. Esta configuración se aplica al ID de la vista, que en este caso es el ID de la vista de pila.Si el usuario toca la vista superior, el widget muestra la
Toast
mensaje "Vista táctil n" dónde n es el índice (posición) de la vista que se toca. Para obtener más información sobre cómo para implementar comportamientos, consulta el artículo Cómo agregar un comportamiento a items.
Cómo implementar widgets con colecciones
Para implementar un widget con colecciones, sigue el procedimiento para implementar cualquier
y, luego, sigue estos pasos adicionales:
modificar el manifiesto, agregar una vista de colección al diseño del widget y modificar tu
la subclase AppWidgetProvider
.
Manifiesto para widgets con colecciones
Más allá de los requisitos enumerados en Declara un widget en el
de Terraform, debes que los widgets que tengan
para vincular a tu RemoteViewsService
. Para ello, declara el
servicio en tu archivo de manifiesto con el permiso
BIND_REMOTEVIEWS
Esto evita que otras aplicaciones accedan libremente a los datos de tu widget.
Por ejemplo, cuando crees un widget que use RemoteViewsService
para propagar un
vista de colección, la entrada del manifiesto podría verse de la siguiente manera:
<service android:name="MyWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
En este ejemplo, android:name="MyWidgetService"
se refiere a tu subclase de
RemoteViewsService
Diseño para widgets con colecciones
El requisito principal para tu archivo en formato XML de diseño de widget es que incluya uno de
las vistas de colección: ListView
, GridView
, StackView
o
AdapterViewFlipper
Este es el archivo widget_layout.xml
de la
StackWidget
muestra:
<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>
Ten en cuenta que las vistas vacías deben ser equivalentes a la vista de colección para la que la vista vacía representa un estado vacío.
Además del archivo de diseño para todo el widget, crea otro diseño
que define el diseño de cada elemento de la colección, por ejemplo,
un diseño para cada libro de una colección de libros. La muestra StackWidget
tiene
solo un archivo de diseño de elemento, widget_item.xml
, ya que todos los elementos usan el mismo
.
Clase AppWidgetProvider para widgets con colecciones
Al igual que con los widgets comunes, la mayor parte del código
Subclase AppWidgetProvider
normalmente entra en
onUpdate()
La principal diferencia en tu implementación de onUpdate()
cuando creas un
con colecciones es que debes llamar a
setRemoteAdapter()
Esto le indica a la vista de colección dónde debe obtener sus datos.
Luego, RemoteViewsService
puede mostrar tu implementación de
RemoteViewsFactory
, y el widget puede entregar los datos adecuados. Cuando
llama a este método, pasa un intent que apunte a tu implementación de
RemoteViewsService
y el ID del widget que especifica el widget que se actualizará.
Por ejemplo, a continuación, se muestra cómo la muestra de StackWidget
implementa onUpdate()
.
método de devolución de llamada para establecer RemoteViewsService
como el adaptador remoto para el
colección 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); }
Cómo conservar los datos
Como se describe en esta página, la subclase RemoteViewsService
proporciona
el RemoteViewsFactory
que se usa para propagar la vista de colección remota.
En particular, sigue estos pasos:
Subclase
RemoteViewsService
.RemoteViewsService
es el servicio a través de que un adaptador remoto puede solicitarRemoteViews
.En tu subclase
RemoteViewsService
, incluye una clase que implemente elRemoteViewsFactory
.RemoteViewsFactory
es una interfaz para un entre una vista de colección remota, comoListView
,GridView
,StackView
y los datos subyacentes para esa vista. Tu implementación es responsable de crear un objetoRemoteViews
para cada del conjunto de datos. Esta interfaz es un wrapper delgado alrededor deAdapter
.
No puedes depender de una sola instancia de tu servicio, ni de los datos que contenga, para
se mantienen. No almacenes datos en tu RemoteViewsService
a menos que sea estático. Si
quieres que se conserven los datos de tu widget, el mejor enfoque es usar un
ContentProvider
cuyos datos
y persiste más allá
del ciclo de vida del proceso. Por ejemplo, el widget de un supermercado
almacenar el estado de cada artículo de la lista de compras en una ubicación persistente, como una
en la base de datos SQL.
El contenido principal de la implementación de RemoteViewsService
es su
RemoteViewsFactory
, que se describe en la siguiente sección.
Interfaz de RemoteViewsFactory
La clase personalizada que implementa la interfaz RemoteViewsFactory
proporciona lo siguiente:
el widget con los datos para los elementos en su colección. Para ello,
Combina el archivo de diseño XML de tu elemento de widget con una fuente de datos. Esta fuente de
Los datos pueden ser de cualquier cosa, desde una base de datos hasta un simple array. En StackWidget
la fuente de datos es un array de WidgetItems
. El RemoteViewsFactory
funciona como adaptador para unir los datos a la vista de colección remota.
Los dos métodos más importantes que debes implementar para tu
La subclase RemoteViewsFactory
tiene las siguientes características:
onCreate()
y
getViewAt()
El sistema llama a onCreate()
cuando crea la fábrica por primera vez.
Aquí es donde configuras cualquier conexión o cursor a tu fuente de datos. Para
Por ejemplo, la muestra StackWidget
usa onCreate()
para inicializar un array de
Objetos WidgetItem
. Cuando tu widget está activo, el sistema accede a estos
usando su posición de índice en el array y muestra el texto que
que contiene.
Aquí tienes un extracto del RemoteViewsFactory
de la muestra de StackWidget
.
que muestra partes del método 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 + "!")); } ... } ...
El método RemoteViewsFactory
getViewAt()
muestra un objeto RemoteViews
.
correspondiente a los datos en el position
especificado en el conjunto de datos. Aquí tienes
Un extracto de la implementación de RemoteViewsFactory
de la muestra de 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; }
Agrega comportamientos a elementos individuales
En las secciones anteriores, se muestra cómo vincular tus datos a la colección de widgets. Sin embargo, ¿Qué sucede si deseas agregar un comportamiento dinámico a los elementos individuales de tu una vista de colección?
Como se describe en Controla eventos con onUpdate()
, por lo general, se usa
setOnClickPendingIntent()
para establecer el comportamiento de clic de un objeto, como
hacer que un botón inicie Activity
. Sin embargo,
este enfoque no está permitido para vistas secundarias en un elemento individual de la colección.
Por ejemplo, puedes usar setOnClickPendingIntent()
para configurar un botón global.
en el widget de Gmail que inicia la app, por ejemplo, pero no en la
elementos individuales de lista.
Para agregar un comportamiento de clics a elementos individuales de una colección, usa
setOnClickFillInIntent()
Esto implica configurar una plantilla de intents pendientes para
la vista de colección y, a continuación, establecer un intent de relleno en cada elemento de la
mediante tu RemoteViewsFactory
.
En esta sección, se usa el ejemplo de StackWidget
para describir cómo agregar comportamiento a
elementos individuales. En la muestra de StackWidget
, si el usuario toca la vista superior,
el widget muestra el mensaje Toast
"Vista presionada n", donde n es la
índice (posición) de la vista que se presionó. Funciona de la siguiente manera:
El
StackWidgetProvider
: unAppWidgetProvider
subclase, crea un intent pendiente con una acción personalizada llamadaTOAST_ACTION
Cuando el usuario toca una vista, el intent se activa y transmite
TOAST_ACTION
La transmisión de la clase
StackWidgetProvider
intercepta esta transmisiónonReceive()
, y el widget muestra el mensajeToast
para la vista que se toca. Los datos para los elementos de la colección se proporcionan a través delRemoteViewsFactory
medianteRemoteViewsService
.
Configura la plantilla de intents pendientes
El objeto StackWidgetProvider
(un
AppWidgetProvider
)
configura un intent pendiente. Los elementos individuales de una colección no pueden configurar su
propios intents pendientes. En cambio, toda la colección configura un intent pendiente
y los elementos individuales establecen un intent de relleno para crear
el comportamiento de los usuarios
en cada elemento.
Esta clase también recibe la transmisión que se envía cuando el usuario toca un
vista. Procesa este evento en el método onReceive()
. Si el intent es
acción es TOAST_ACTION
, el widget muestra un mensaje Toast
para el
vista.
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); } }
Cómo configurar el intent de relleno
Tu RemoteViewsFactory
debe establecer un intent de relleno en cada elemento del
de elementos no utilizados. Esto permite distinguir la acción individual que se realiza cuando se hace clic
de un elemento determinado. Luego, el intent de relleno se combina con el
PendingIntent
plantilla para determinar
el intent final que se ejecuta cuando se presiona el elemento.
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; } ... }
Mantén actualizados los datos de la colección
En la Figura 2, se ilustra el flujo de actualización en un widget que usa colecciones. Muestra
cómo interactúa el código del widget con el RemoteViewsFactory
y cómo
Actualizaciones de activadores:
Los widgets que usan colecciones pueden proporcionarles a los usuarios contenido actualizado. Para
Por ejemplo, el widget de Gmail les brinda a los usuarios un resumen de su carpeta Recibidos. Para que este
como sea posible, activa tu RemoteViewsFactory
y la vista de colecciones para recuperar y
mostrar datos nuevos.
Para hacerlo, usa el
AppWidgetManager
para llamar
notifyAppWidgetViewDataChanged()
Esta llamada genera una devolución de llamada a la API de tu objeto RemoteViewsFactory
.
onDataSetChanged()
que te permite recuperar cualquier dato nuevo.
Puedes realizar operaciones de procesamiento intensivo de manera síncrona dentro del
onDataSetChanged()
. Tienes la garantía de que esta llamada se completará
antes de que los metadatos o los datos de vista se recuperen del RemoteViewsFactory
. Tú
también puede realizar operaciones de procesamiento intensivo dentro de getViewAt()
. Si esta llamada demora mucho, la vista de carga, especificada por el
RemoteViewsFactory
objeto
getLoadingView()
se muestra en la posición correspondiente de la vista de colecciones.
hasta que regrese.
Usa RemoteCollectionItems para pasar una colección directamente
Android 12 (nivel de API 31) agrega la setRemoteAdapter(int viewId,
RemoteViews.RemoteCollectionItems
items)
que le permite a tu app pasar una colección directamente cuando se propaga una
vista de colección. Si configuras el adaptador con este método, no es necesario
implementa un RemoteViewsFactory
y no necesitas llamar
notifyAppWidgetViewDataChanged()
Además de facilitar la propagación de tu adaptador, este enfoque también
quita la latencia para propagar elementos nuevos cuando los usuarios se desplazan hacia abajo en la lista para
revelar un nuevo elemento. Se prefiere este enfoque para configurar el adaptador, siempre y cuando
el conjunto de elementos de tu colección
es relativamente pequeño. Sin embargo, por ejemplo, esta
no funciona bien si tu colección contiene varios Bitmaps
que
pasado a setImageViewBitmap
.
Si la colección no usa un conjunto constante de diseños, es decir, si
los elementos solo están presentes a veces; usa setViewTypeCount
para especificar la
la cantidad máxima de diseños únicos que puede contener la colección. Esto permite que las
el adaptador se reutilizará en las actualizaciones del widget de la app.
A continuación, se muestra un ejemplo de cómo implementar colecciones RemoteViews
simplificadas.
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() );