Cómo crear un widget simple

Los widgets de apps son vistas en miniatura de una app que puedes incorporar en otros aplicaciones, como la pantalla de inicio, y recibir actualizaciones periódicas. Estos se denominan widgets en la interfaz de usuario, y puedes publicar uno con el proveedor de widgets de la app (o el proveedor de widgets). Un componente de la aplicación que que contiene otros widgets se denomina host de widget de la app (o host de widget). Figura 1 muestra un widget de música de muestra:

Ejemplo de widget de música
Figura 1: Ejemplo de un widget de música

En este documento, se describe cómo publicar un widget usando un proveedor de widgets. Para detalles sobre cómo crear tu propio AppWidgetHost para widgets de apps host, consulta Cómo compilar un host de widgets.

Para obtener información sobre cómo diseñar el widget, consulta Descripción general de los widgets de apps.

Componentes del widget

Para crear un widget, necesitas los siguientes componentes básicos:

Objeto AppWidgetProviderInfo
Describe los metadatos para un widget, como su diseño, la actualización la frecuencia y la clase AppWidgetProvider. AppWidgetProviderInfo se define en XML, como que se describe en este documento.
Clase AppWidgetProvider
Define los métodos básicos que te permiten interactuar de manera programática con el widget. A través de él, recibes emisiones cuando se actualiza el widget habilitar, inhabilitar o borrar. Declaras AppWidgetProvider en las en el manifiesto y, luego, implementarlo, que se describe en este documento.
Diseño de la vista
Define el diseño inicial del widget. El diseño se define en XML, como se describe en este documento.

En la Figura 2, se muestra cómo se ajustan estos componentes al procesamiento general del widget de la app. de tu flujo de trabajo.

Flujo de procesamiento del widget de la app
Figura 2: Flujo de procesamiento del widget de la app.

Si tu widget necesita que el usuario configure el widget de la app, actividad. Esta actividad permite que los usuarios modifiquen la configuración del widget; por ejemplo, el la zona horaria para un widget de reloj.

También recomendamos las siguientes mejoras: diseños flexibles de widgets, mejoras varias, widgets avanzados, widgets de colecciones y creación de widgets. del servidor.

Declara el XML de AppWidgetProviderInfo

El objeto AppWidgetProviderInfo define las cualidades esenciales de un widget. Define el objeto AppWidgetProviderInfo en un archivo de recursos XML con un solo elemento <appwidget-provider> y guárdalo en la carpeta res/xml/ del proyecto.

Esto se muestra en el siguiente ejemplo:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:targetCellWidth="1"
    android:targetCellHeight="1"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="120dp"
    android:updatePeriodMillis="86400000"
    android:description="@string/example_appwidget_description"
    android:previewLayout="@layout/example_appwidget_preview"
    android:initialLayout="@layout/example_loading_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

Atributos de tamaño de widgets

La pantalla principal predeterminada coloca los widgets en su ventana en función de una cuadrícula de celdas. que tienen una altura y un ancho definidos. La mayoría de las pantallas principales solo permiten que los widgets adopten tamaños que son múltiplos enteros de las celdas de la cuadrícula, por ejemplo, dos celdas horizontalmente por tres celdas en vertical.

Los atributos de tamaño del widget te permiten especificar un tamaño predeterminado para este y proporcionar límites inferior y superior para el tamaño del widget. En este contexto, el tamaño predeterminado de un widget es el tamaño que este toma cuando se inicia por primera vez agregado a la pantalla de inicio.

En la siguiente tabla, se describen los atributos <appwidget-provider> correspondientes tamaño del widget:

Atributos y descripción
targetCellWidth y targetCellHeight (Android 12), minWidth y minHeight
  • A partir de Android 12, la targetCellWidth y targetCellHeight los atributos especifican el tamaño predeterminado del widget según la cuadrícula celdas. Estos atributos se ignoran en Android 11 y inferiores, y pueden ignorarse si la pantalla principal no admiten un diseño basado en cuadrículas.
  • Los operadores minWidth y Los atributos minHeight especifican el tamaño predeterminado del widget en dp. Si los valores del ancho o la altura mínimos de un widget no coinciden las dimensiones de las celdas, los valores se redondean tamaño de celda más cercano.
Recomendamos especificar ambos conjuntos atributos: targetCellWidth y targetCellHeight, minWidth y minHeight, para que tu app pueda volver a usar minWidth y minHeight si el dispositivo del usuario no es compatible con targetCellWidth y targetCellHeight Si es compatible, el Atributos targetCellWidth y targetCellHeight tienen prioridad sobre minWidth y minHeight atributos.
minResizeWidth y minResizeHeight Especifica el tamaño mínimo absoluto del widget. Estos valores especifican la tamaño en el que el widget es ilegible o inutilizable. Usando Estos atributos permiten al usuario cambiar el tamaño del widget a un tamaño menor. que el tamaño predeterminado del widget. El atributo minResizeWidth tiene las siguientes características: se ignora si es mayor que minWidth o si es horizontal el cambio de tamaño no está habilitado. Consulta resizeMode Del mismo modo, el el atributo minResizeHeight se ignora si es mayor que minHeight o si no está habilitado el cambio de tamaño vertical.
maxResizeWidth y maxResizeHeight Especifica el tamaño máximo recomendado del widget. Si los valores no son un múltiplo de las dimensiones de las celdas de la cuadrícula, se redondean hasta la más cercana tamaño de celda. El atributo maxResizeWidth se ignora si es inferior a minWidth o si el cambio de tamaño horizontal no es habilitado. Consulta resizeMode. Del mismo modo, el atributo maxResizeHeight se ignora si es mayor que minHeight o si el cambio de tamaño vertical no está habilitado. Se introdujo en Android 12.
resizeMode Especifica las reglas mediante las cuales se puede cambiar el tamaño de un widget. Puedes usar esta para hacer que los widgets de la pantalla principal puedan cambiar de tamaño de forma horizontal o vertical. o en ambos ejes. Los usuarios toquen y mantén presionado un widget para mostrar sus controladores de cambio de tamaño, luego arrastra los controladores horizontales o verticales para cambiar su tamaño en la cuadrícula de diseño. Entre los valores del atributo resizeMode, se incluyen los siguientes: horizontal, vertical y none. Para declara que un widget puede cambiar de tamaño horizontal y verticalmente, usa horizontal|vertical

Ejemplo

Para ilustrar cómo los atributos de la tabla anterior afectan el tamaño del widget, supón las siguientes especificaciones:

  • Una celda de cuadrícula tiene 30 dp de ancho y 50 dp de alto.
  • Se proporciona la siguiente especificación de atributo:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="80dp"
    android:minHeight="80dp"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:maxResizeWidth="120dp"
    android:maxResizeHeight="120dp"
    android:resizeMode="horizontal|vertical" />

A partir de Android 12:

Usa los atributos targetCellWidth y targetCellHeight como valores predeterminados. tamaño del widget.

De forma predeterminada, el tamaño del widget es 2x2. Se puede reducir el tamaño del widget a 2 x 1. de hasta 4 × 3.

Android 11 y versiones anteriores:

Usa los atributos minWidth y minHeight para calcular el tamaño predeterminado de el widget.

El ancho predeterminado = Math.ceil(80 / 30) = 3

La altura predeterminada = Math.ceil(80 / 50) = 2

De forma predeterminada, el tamaño del widget es 3x2. Se puede reducir el tamaño del widget a 2 x 1. en pantalla completa.

Atributos adicionales del widget

En la siguiente tabla, se describen los atributos <appwidget-provider> correspondientes a cualidades que no sean el tamaño de widgets.

Atributos y descripción
updatePeriodMillis Define la frecuencia con la que el framework del widget solicita una actualización desde el AppWidgetProvider llamando a onUpdate() de devolución de llamada. No se garantiza que la actualización real ocurra exactamente el tiempo con este valor, y te recomendamos que actualices posible, no más de una vez por hora, para conservar la batería. Para ver la lista completa de consideraciones para elegir un período de actualización adecuado, ver Optimizaciones para actualizar el widget contenido.
initialLayout Apunta al recurso de diseño que define el diseño del widget.
configure Define la actividad que se inicia cuando el usuario agrega el widget. lo que les permite configurar las propiedades de los widgets. Consulta Permite que los usuarios configuren widgets. A partir de Android 12, la app puede omitir la configuración. Consulta Usa el configuración predeterminada del widget para obtener más detalles.
description Especifica la descripción para que el selector del widget se muestre para tu widget. Se introdujo en Android 12.
previewLayout (Android 12) y previewImage (Android 11 y versiones anteriores)
  • A partir de Android 12, la El atributo previewLayout especifica una vista previa escalable, que que proporcionas como diseño XML, configurado en el tamaño predeterminado del widget. Idealmente, el XML de diseño especificado como este atributo es el mismo XML de diseño que el widget real con valores predeterminados realistas.
  • En Android 11 o versiones anteriores, previewImage especifica una vista previa del aspecto del widget después de que el usuario ve cuando selecciona el widget de la app. Si no es así se proporciona, el usuario ve el ícono del selector de tu app. Esta corresponde al atributo android:previewImage en el elemento <receiver> en la Archivo AndroidManifest.xml.
Nota: Te recomendamos especificar tanto previewImage. y previewLayout para que tu app pueda recurrir al uso de previewImage si el dispositivo del usuario no es compatible previewLayout Para obtener más detalles, consulta Retrocompatibilidad con modelos vistas previas de widgets.
autoAdvanceViewId Especifica el ID de vista de la subvista del widget que avanza automáticamente el host del widget.
widgetCategory Declara si tu widget se puede mostrar en la pantalla principal. (home_screen), la pantalla de bloqueo (keyguard) o ambos. Para Android 5.0 y versiones posteriores, solo es válido home_screen.
widgetFeatures Declara las funciones compatibles con el widget. Por ejemplo, si quieres que tu widget use su configuración predeterminada cuando un usuario lo agregue, especifica tanto configuration_optional y reconfigurable marcas. De esta manera, se evita iniciar la actividad de configuración después de que un usuario agrega el widget. El usuario aún puede reconfigura el widget después.

Cómo usar la clase AppWidgetProvider para controlar las transmisiones del widget

La clase AppWidgetProvider controla las transmisiones del widget y lo actualiza. en respuesta a eventos de ciclo de vida de widgets. En las siguientes secciones, se describe cómo declara AppWidgetProvider en el manifiesto y, luego, impleméntalo.

Declara un widget en el manifiesto

Primero, declara la clase AppWidgetProvider en el archivo AndroidManifest.xml de tu app. como se muestra en el siguiente ejemplo:

<receiver android:name="ExampleAppWidgetProvider"
                 android:exported="false">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/example_appwidget_info" />
</receiver>

El elemento <receiver> requiere el atributo android:name, que especifica el AppWidgetProvider que usa el widget. No se debe exportar el componente a menos que se deba transmitir un proceso separado a tu AppWidgetProvider, lo que no suele ser el caso.

El elemento <intent-filter> debe incluir un elemento <action> con el atributo android:name. Este atributo especifica que el AppWidgetProvider acepta el ACTION_APPWIDGET_UPDATE transmisión. Esta es la única emisión que debes declarar de manera explícita. El AppWidgetManager envía automáticamente todas las demás transmisiones de widgets al AppWidgetProvider como necesario.

El elemento <meta-data> especifica el recurso AppWidgetProviderInfo y requiere los siguientes atributos:

  • android:name: Especifica el nombre de los metadatos. Usa android.appwidget.provider para identificar los datos Descriptor AppWidgetProviderInfo.
  • android:resource: Especifica el recurso AppWidgetProviderInfo. ubicación.

Implementa la clase AppWidgetProvider

La clase AppWidgetProvider extiende BroadcastReceiver como de conveniencia para manejar las transmisiones de widgets. Solo recibe el evento relevantes para el widget, como cuando se actualiza el widget, borrar, habilitar e inhabilitar. Cuando ocurren estos eventos de transmisión, Se llama a los métodos AppWidgetProvider de la siguiente manera:

onUpdate()
Se llama a este método para actualizar el widget en intervalos definidos por el El atributo updatePeriodMillis en AppWidgetProviderInfo Consulta la tabla atributos adicionales del widget en esta página para más información.
También se llama a este método cuando el usuario agrega el widget, de modo que realiza configuración esencial, como la definición de controladores de eventos para Objetos View o trabajos iniciales a los que se cargarán datos mostrar en el widget. Sin embargo, si declaras una actividad de configuración sin la marca configuration_optional, no se llama a este método cuando el usuario agrega el widget, pero se llama para las actualizaciones posteriores. Es el responsabilidad de la actividad de configuración para realizar la primera actualización cuando configuración esté completa. Consulta Cómo permitir que los usuarios configuren widgets de apps para obtener más información.
La devolución de llamada más importante es onUpdate(). Consulta Controla eventos con el Clase onUpdate() de esta página para obtener más información.
onAppWidgetOptionsChanged()

Se llama a este método cuando se coloca el widget por primera vez y cada vez que se lo coloca se ha cambiado el tamaño. Usa esta devolución de llamada para ocultar o mostrar contenido según el tamaño del widget o rangos. Obtén los rangos de tamaño y, a partir de Android 12, la lista de tamaños posibles que puede tomar una instancia de widget, llamando getAppWidgetOptions(): que devuelve un Bundle que incluye el lo siguiente:

onDeleted(Context, int[])

Se llama a este método cada vez que se borra un widget del host del widget.

onEnabled(Context)

Se llama a este método cuando se crea una instancia del widget por primera vez. Por ejemplo, si el usuario agrega dos instancias de tu widget, esto solo se llamará primera vez. Si necesitas abrir una base de datos nueva o realizar otra configuración que solo debe ocurrir una vez para todas las instancias de widgets, este es un buen lugar para hazlo.

onDisabled(Context)

Se llama a este método cuando se borra la última instancia de tu widget desde la host de widgets. Aquí es donde limpias cualquier trabajo realizado en onEnabled(Context), como borrar una base de datos temporal.

onReceive(Context, Intent)

Se llama a este método para cada transmisión y antes de cada devolución de llamada anterior. . Por lo general, no es necesario implementar este método, ya que la configuración La implementación de AppWidgetProvider filtra todas las transmisiones de widgets y llama al métodos anteriores según corresponda.

Debes declarar tu implementación de la clase AppWidgetProvider como una transmisión. con el elemento <receiver> en AndroidManifest. Consulta Cómo declarar un en el manifiesto de esta página para obtener más información.

Cómo controlar eventos con la clase onUpdate()

La devolución de llamada AppWidgetProvider más importante es onUpdate(), porque es se llama cuando cada widget se agrega a un host, a menos que uses una configuración sin la marca configuration_optional. Si el widget acepta alguna de interacción del usuario y, luego, registra los controladores de eventos en esta devolución de llamada. Si tu widget no crea archivos temporales o bases de datos, ni realiza otras tareas que requiere limpieza, entonces onUpdate() podría ser el único método de devolución de llamada que definir.

Por ejemplo, si quieres un widget con un botón que inicie una actividad cuando puedes usar la siguiente implementación de AppWidgetProvider:

Kotlin

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity.
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                    /* context = */ context,
                    /* requestCode = */  0,
                    /* intent = */ Intent(context, ExampleActivity::class.java),
                    /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            val views: RemoteViews = RemoteViews(
                    context.packageName,
                    R.layout.appwidget_provider_layout
            ).apply {
                setOnClickPendingIntent(R.id.button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

Java

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        for (int i=0; i < appWidgetIds.length; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                /* context = */ context,
                /* requestCode = */ 0,
                /* intent = */ intent,
                /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

Este AppWidgetProvider define solo el método onUpdate() y lo usa para lo siguiente: crea un PendingIntent que inicie un objeto Activity y lo adjunta al archivo con setOnClickPendingIntent(int, PendingIntent). Incluye un bucle que se itera a través de cada entrada en appWidgetIds, que es un array de IDs que identifican cada widget creado por este proveedor. Si el usuario crea más de una instancia del widget, entonces se actualizan todos al mismo tiempo. Sin embargo, solo hay un programa de updatePeriodMillis se administra para todas las instancias del widget. Por ejemplo, si el cronograma de actualización se define para que sea cada dos horas, y se agrega una segunda instancia del widget una hora después de la primera, ambas se actualizarán en el período definido por el primero, y el segundo período de actualización se ignora. Se actualizan cada dos horas, no cada hora.

Consulta la ExampleAppWidgetProvider.java para obtener más información.

Recibir intents de transmisión de widget

AppWidgetProvider es una clase de conveniencia. Si deseas recibir el widget directamente, puedes implementar tu propio BroadcastReceiver o anular el Devolución de llamada onReceive(Context,Intent). Los intents a los que debes prestar atención son los lo siguiente:

Cómo crear el diseño del widget

Debes definir un diseño inicial para tu widget en XML y guardarlo en el directorio res/layout/ del proyecto. Consulta Diseño lineamientos para obtener más información.

Crear el diseño del widget es sencillo si conoces diseños. Sin embargo, ten en cuenta que el widget los diseños se basan en RemoteViews, que no es compatible con todo tipo de diseño o widget de vista. No puedes usar la función Vistas o subclases de las vistas compatibles con RemoteViews.

RemoteViews también admite ViewStub, un objeto View invisible de tamaño cero que puedes usar para aumentar el diseño de forma diferida. recursos en el entorno de ejecución.

Compatibilidad con comportamientos con estado

En Android 12, se agrega compatibilidad con comportamientos con estado mediante lo siguiente componentes existentes:

El widget todavía no tiene un estado. La app debe almacenar el estado y registrarse para los eventos de cambio de estado.

Ejemplo de un widget de lista de compras que muestra el comportamiento con estado
Figura 3: Ejemplo de comportamiento con estado.

En el siguiente ejemplo de código, se muestra cómo implementar estos componentes.

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
        R.id.my_checkbox,
        RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));

Proporciona dos diseños: uno orientado a dispositivos que ejecutan Android 12 o más alto en res/layout-v31 y la anterior Android 11 o versiones anteriores en la carpeta predeterminada res/layout

Cómo implementar esquinas redondeadas

Android 12 presenta los siguientes parámetros del sistema para establecer la radios de las esquinas redondeadas de tu widget:

  • system_app_widget_background_radius: el radio de la esquina del fondo del widget, que nunca es mayor que 28 dp.

  • system_app_widget_inner_radius: el radio de la esquina de cualquier vista dentro del widget. Significa exactamente 8 dp menor que el radio de fondo, para alinearse bien cuando se usa un valor de 8 dp con padding.

En el siguiente ejemplo, se muestra un widget que usa system_app_widget_background_radius para su esquina y system_app_widget_inner_radius para las vistas dentro de este.

Widget que muestra los radios del fondo y las vistas del widget
Figura 4: Esquinas redondeadas.

1 Esquina del widget.

2 Esquina de una vista dentro del widget.

Consideraciones importantes sobre las esquinas redondeadas

  • Los selectores externos y los fabricantes de dispositivos pueden anular la system_app_widget_background_radius tenga un tamaño inferior a 28 dp. El parámetro system_app_widget_inner_radius siempre es 8 dp menor que el valor de system_app_widget_background_radius
  • Si el widget no usa @android:id/background ni define un fondo que recorta su contenido en función del esquema, con android:clipToOutline establecido en true, el selector identifica automáticamente el fondo y recorta el widget con un rectángulo con esquinas redondeadas de hasta 16 dp. Consulta Asegúrate de que el widget sea compatible con Android 12

Para ofrecer compatibilidad de widgets con versiones anteriores de Android, recomendamos definir atributos personalizados y usar un tema personalizado para anularlos Android 12, como se muestra en los siguientes archivos en formato XML de ejemplo:

/values/attrs.xml

<resources>
  <attr name="backgroundRadius" format="dimension" />
</resources>

/values/styles.xml

<resources>
  <style name="MyWidgetTheme">
    <item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
  </style>
</resources>

/values-31/styles.xml

<resources>
  <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
    <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
  </style>
</resources>

/drawable/my_widget_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">
  <corners android:radius="?attr/backgroundRadius" />
  ...
</shape>

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />