En esta página, se describen las mejoras para el tamaño de los widgets y la mayor flexibilidad que se introdujeron en Android 12 (nivel de API 31). También se detalla cómo determinar un tamaño para tu widget.
Cómo usar API mejoradas para tamaños y diseños de widgets
A partir de Android 12 (nivel de API 31), puedes brindar atributos de tamaño más definidos y diseños flexibles haciendo lo siguiente, como se describe en las siguientes secciones:
Especifica restricciones adicionales para el tamaño de widgets.
Brinda diseños responsivos o exactos layouts.
En versiones anteriores de Android, es posible obtener los rangos de tamaño de un
widget con los extras
OPTION_APPWIDGET_MIN_WIDTH,
OPTION_APPWIDGET_MIN_HEIGHT,
OPTION_APPWIDGET_MAX_WIDTH,
y OPTION_APPWIDGET_MAX_HEIGHT
y, luego, estimar el tamaño del widget, pero esa lógica no funciona en todas las
situaciones. Para los widgets que se orientan a Android 12 o versiones posteriores, te recomendamos que brindes diseños responsivos o exactos.
Especifica restricciones adicionales para el tamaño de widgets
Android 12 agrega APIs que te permiten garantizar que el tamaño de tu widget sea más confiable en diferentes dispositivos con tamaños de pantalla variables.
Además de los atributos existentes minWidth,
minHeight,
minResizeWidth,
y minResizeHeight, usa los siguientes atributos nuevos appwidget-provider:
targetCellWidthytargetCellHeight: definen el tamaño de destino del widget en términos de las celdas de cuadrícula del launcher. Si se definen estos atributos, se usan en lugar deminWidthominHeight.maxResizeWidthymaxResizeHeight: definen el tamaño máximo del launcher que le permite al usuario cambiar el tamaño del widget.
En el siguiente XML, se muestra cómo usar los atributos de tamaño.
<appwidget-provider
...
android:targetCellWidth="3"
android:targetCellHeight="2"
android:maxResizeWidth="250dp"
android:maxResizeHeight="110dp">
</appwidget-provider>
Cómo brindar diseños responsivos
Si el diseño necesita cambiar en función del tamaño del widget, te recomendamos que crees un conjunto pequeño de diseños y que cada uno sea válido para una variedad de tamaños. Si esto no es posible, otra opción es proporcionar diseños basados en el tamaño exacto del widget en el tiempo de ejecución, como se describe en esta página.
Esta función permite un escalamiento más fluido y un mejor estado general del sistema, ya que no es necesario que el sistema active la app cada vez que muestra el widget en un tamaño diferente.
En el siguiente ejemplo de código, se muestra cómo brindar una lista de diseños.
Kotlin
override fun onUpdate(...) { val smallView = ... val tallView = ... val wideView = ... val viewMapping: Map<SizeF, RemoteViews> = mapOf( SizeF(150f, 100f) to smallView, SizeF(150f, 200f) to tallView, SizeF(215f, 100f) to wideView ) val remoteViews = RemoteViews(viewMapping) appWidgetManager.updateAppWidget(id, remoteViews) }
Java
@Override public void onUpdate(...) { RemoteViews smallView = ...; RemoteViews tallView = ...; RemoteViews wideView = ...; Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); viewMapping.put(new SizeF(150f, 100f), smallView); viewMapping.put(new SizeF(150f, 200f), tallView); viewMapping.put(new SizeF(215f, 100f), wideView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); }
Supongamos que el widget tiene los siguientes atributos:
<appwidget-provider
android:minResizeWidth="160dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="250dp"
android:maxResizeHeight="200dp">
</appwidget-provider>
El fragmento de código anterior significa lo siguiente:
smallViewadmite de 160 dp (minResizeWidth) × 110 dp (minResizeHeight) a 160 dp × 199 dp (próximo punto de corte - 1 dp).tallViewadmite de 160 dp × 200 dp a 214 dp (próximo punto de corte - 1) × 200 dp.wideViewadmite de 215 dp × 110 dp (minResizeHeight) a 250 dp (maxResizeWidth) × 200 dp (maxResizeHeight).
Tu widget debe admitir el rango de tamaño de minResizeWidth × minResizeHeight a maxResizeWidth × maxResizeHeight. Dentro de ese rango, puedes decidir el punto de corte para cambiar los diseños.
Cómo brindar diseños exactos
Si no es posible proporcionar un conjunto pequeño de diseños responsivos, en su lugar, puedes brindar diferentes diseños que se adapten a los tamaños en los que se muestra el widget. Suelen ser dos tamaños para teléfonos (modo de retrato y de paisaje) y cuatro tamaños para dispositivos plegables.
Para implementar esta solución, la app debe realizar los siguientes pasos:
Sobrecargar a
AppWidgetProvider.onAppWidgetOptionsChanged(), al que se llama cuando cambia el conjunto de tamaños.Llamar a
AppWidgetManager.getAppWidgetOptions(), que muestraBundlecon los tamaños.Acceder a la clave
AppWidgetManager.OPTION_APPWIDGET_SIZESdesdeBundle.
En el siguiente ejemplo de código, se muestra cómo brindar diseños exactos.
Kotlin
override fun onAppWidgetOptionsChanged( context: Context, appWidgetManager: AppWidgetManager, id: Int, newOptions: Bundle? ) { super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions) // Get the new sizes. val sizes = newOptions?.getParcelableArrayList<SizeF>( AppWidgetManager.OPTION_APPWIDGET_SIZES ) // Check that the list of sizes is provided by the launcher. if (sizes.isNullOrEmpty()) { return } // Map the sizes to the RemoteViews that you want. val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews)) appWidgetManager.updateAppWidget(id, remoteViews) } // Create the RemoteViews for the given size. private fun createRemoteViews(size: SizeF): RemoteViews { }
Java
@Override public void onAppWidgetOptionsChanged( Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions); // Get the new sizes. ArrayList<SizeF> sizes = newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES); // Check that the list of sizes is provided by the launcher. if (sizes == null || sizes.isEmpty()) { return; } // Map the sizes to the RemoteViews that you want. Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); for (SizeF size : sizes) { viewMapping.put(size, createRemoteViews(size)); } RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews); } // Create the RemoteViews for the given size. private RemoteViews createRemoteViews(SizeF size) { }
Determina un tamaño para tu widget
Cada widget debe definir un targetCellWidth y un targetCellHeight para dispositivos que ejecutan Android 12 o versiones posteriores, o bien minWidth y minHeight para todas las versiones de Android, lo que indica la cantidad mínima de espacio que consume de forma predeterminada. Sin embargo, cuando los usuarios agregan un widget a su pantalla principal, generalmente ocupa más que el ancho y la altura mínimos que especificas.
Las pantallas principales de Android ofrecen a los usuarios una cuadrícula de espacios disponibles en los que pueden ubicar íconos y widgets. Esta cuadrícula puede variar según el dispositivo; por ejemplo, muchos teléfonos ofrecen una cuadrícula de 5 x 4, mientras que las tablets pueden ofrecer una cuadrícula más grande. Cuando se agrega tu widget, se expande para ocupar la cantidad mínima de celdas, horizontal y verticalmente, que necesite para satisfacer las restricciones de targetCellWidth y targetCellHeight en dispositivos que ejecutan Android 12 o versiones posteriores, o bien las restricciones de minWidth y minHeight en dispositivos que ejecutan Android 11 (nivel de API 30) o versiones anteriores.
Tanto el ancho como la altura de una celda y el tamaño de los márgenes automáticos aplicados a los widgets pueden variar según los dispositivos. Usa la siguiente tabla para estimar aproximadamente las dimensiones mínimas de tu widget en un teléfono típico con cuadrícula de 5 x 4, según la cantidad de celdas de cuadrícula ocupadas que desees:
| Cantidad de celdas (ancho x alto) | Tamaño disponible en modo vertical (dp) | Tamaño disponible en modo horizontal (dp) |
|---|---|---|
| 1x1 | 57x102dp | 127x51dp |
| 2x1 | 130x102dp | 269x51dp |
| 3x1 | 203x102dp | 412x51dp |
| 4x1 | 276x102dp | 554x51dp |
| 5x1 | 349x102dp | 697x51dp |
| 5x2 | 349x220dp | 697x117dp |
| 5x3 | 349x337dp | 697x184dp |
| 5x4 | 349x455dp | 697x250dp |
| … | … | … |
| n x m | (73n - 16) x (118m - 16) | (142n - 15) x (66m - 15) |
Usa los tamaños de celda del modo vertical para informar los valores que proporcionas para los atributos minWidth, minResizeWidth y maxResizeWidth. Del mismo modo, usa los tamaños de celda del modo horizontal para informar los valores que proporcionas para los atributos minHeight, minResizeHeight y maxResizeHeight.
El motivo es que el ancho de la celda suele ser más pequeño en el modo vertical que en el modo horizontal y, de manera similar, la altura de la celda suele ser más pequeña en el modo horizontal que en el modo vertical.
Por ejemplo, si quieres que el ancho de tu widget se pueda cambiar de tamaño a una celda en un Google Pixel 4, debes configurar tu minResizeWidth en 56 dp como máximo para asegurarte de que el valor del atributo minResizeWidth sea menor que 57 dp, ya que una celda tiene al menos 57 dp de ancho en modo vertical.
Del mismo modo, si quieres que la altura de tu widget se pueda cambiar de tamaño en una celda en el mismo dispositivo, debes configurar tu minResizeHeight en 50 dp como máximo para asegurarte de que el valor del atributo minResizeHeight sea menor que 51 dp, ya que una celda tiene al menos 51 dp de altura en modo horizontal.
Cada widget se puede cambiar de tamaño dentro de los rangos de tamaño entre los atributos minResizeWidth/minResizeHeight y maxResizeWidth/maxResizeHeight, lo que significa que debe adaptarse a cualquier rango de tamaño entre ellos.
Por ejemplo, para establecer el tamaño predeterminado del widget en la ubicación, puedes configurar los siguientes atributos:
<appwidget-provider
android:targetCellWidth="3"
android:targetCellHeight="2"
android:minWidth="180dp"
android:minHeight="110dp">
</appwidget-provider>
Esto significa que el tamaño predeterminado del widget es de 3x2 celdas, como se especifica en los atributos targetCellWidth y targetCellHeight, o de 180 × 110 dp, como se especifica en minWidth y minHeight para dispositivos que ejecutan Android 11 o versiones anteriores. En este último caso, el tamaño en celdas puede variar según el dispositivo.
Además, para establecer los rangos de tamaño admitidos de tu widget, puedes configurar los siguientes atributos:
<appwidget-provider
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:maxResizeWidth="530dp"
android:maxResizeHeight="450dp">
</appwidget-provider>
Como se especifica en los atributos anteriores, el ancho del widget se puede cambiar de tamaño de 180 dp a 530 dp, y su altura se puede cambiar de tamaño de 110 dp a 450 dp. Luego, el widget se puede cambiar de tamaño de 3x2 a 5x2 celdas, siempre que se cumplan las siguientes condiciones:
- El dispositivo tiene la cuadrícula de 5 x 4.
- La asignación entre la cantidad de celdas y el tamaño disponible en dps sigue la tabla que muestra la estimación de las dimensiones mínimas en esta página.
- El widget se adapta a ese rango de tamaño.
Kotlin
val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small) val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium) val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large) val viewMapping: Map<SizeF, RemoteViews> = mapOf( SizeF(180f, 110f) to smallView, SizeF(270f, 110f) to mediumView, SizeF(270f, 280f) to largeView ) appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))
Java
RemoteViews smallView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small); RemoteViews mediumView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium); RemoteViews largeView = new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large); Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>(); viewMapping.put(new SizeF(180f, 110f), smallView); viewMapping.put(new SizeF(270f, 110f), mediumView); viewMapping.put(new SizeF(270f, 280f), largeView); RemoteViews remoteViews = new RemoteViews(viewMapping); appWidgetManager.updateAppWidget(id, remoteViews);
Supongamos que el widget usa los diseños responsivos definidos en los fragmentos de código anteriores. Esto significa que el diseño especificado como R.layout.widget_weather_forecast_small se usa de 180 dp (minResizeWidth) x 110 dp (minResizeHeight) a 269x279 dp (próximos puntos de corte - 1). Del mismo modo, R.layout.widget_weather_forecast_medium se usa de 270x110 dp a 270x279 dp, y R.layout.widget_weather_forecast_large se usa de 270x280 dp a 530 dp (maxResizeWidth) x 450 dp (maxResizeHeight).
A medida que el usuario cambia el tamaño del widget, su apariencia cambia para adaptarse a cada tamaño en celdas, como se muestra en los siguientes ejemplos.
R.layout.widget_weather_forecast_small.
R.layout.widget_weather_forecast_medium.
R.layout.widget_weather_forecast_medium.
R.layout.widget_weather_forecast_large.
R.layout.widget_weather_forecast_large.