Oferecer layouts de widget flexíveis

Esta página descreve refinamentos para dimensionamento e maior flexibilidade de widgets introduzido no Android 12 (nível 31 da API). Ele também detalha como determinar um tamanho para o widget.

Usar APIs melhoradas para tamanhos e layouts de widgets

No Android 12 (nível 31 da API) e versões mais recentes, você pode oferecer tamanhos mais refinados atributos e layouts flexíveis fazendo o seguinte, conforme descrito nas seções a seguir:

  1. Especifique outras restrições de dimensionamento do widget.

  2. Fornecer layouts responsivos ou recursos exatos layouts.

Em versões anteriores do Android, era possível obter os intervalos de tamanho de um widget usando o OPTION_APPWIDGET_MIN_WIDTH, OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH, e OPTION_APPWIDGET_MAX_HEIGHT extras e estima o tamanho do widget, mas essa lógica não funciona em diferentes situações. Para widgets destinados ao Android 12 ou versões mais recentes, recomendamos fornecendo soluções responsivas ou exatas layouts.

Especificar outras restrições de dimensionamento do widget

O Android 12 adiciona APIs para que você garanta que seu widget seja dimensionados de maneira mais confiável em diferentes dispositivos com diferentes tamanhos de tela.

Além dos atributos minWidth, minHeight, minResizeWidth, e minResizeHeight, use os novos atributos appwidget-provider a seguir:

  • targetCellWidth e targetCellHeight: define o tamanho de destino do widget em termos de células da grade da tela de início. Se definidos, esses atributos serão usados em vez de minWidth ou minHeight.

  • maxResizeWidth e maxResizeHeight: definem o tamanho máximo para o qual a tela de início permite que o usuário redimensione o widget.

O XML a seguir mostra como usar os atributos de dimensionamento.

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

Fornecer layouts responsivos

Se o layout precisar mudar dependendo do tamanho do widget, recomendamos criar um pequeno conjunto de layouts, cada um válido para uma série de tamanhos. Se esse não for possível, outra opção é fornecer layouts com base no widget exato no tempo de execução, conforme descrito nesta página.

Esse recurso permite um escalonamento mais suave e um sistema melhor em geral integridade, porque o sistema não precisa ativar o aplicativo todas as vezes o widget será exibido em um tamanho diferente.

O exemplo de código a seguir mostra como fornecer uma lista de layouts.

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);
}

Suponha que o widget tenha os seguintes atributos:

<appwidget-provider
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>

O snippet de código anterior significa o seguinte:

  • smallView oferece suporte a 160 dp (minResizeWidth) × 110 dp (minResizeHeight) para 160 dp × 199 dp (próximo ponto de corte - 1 dp).
  • tallView oferece suporte de 160 dp × 200 dp a 214 dp (próximo ponto de corte - 1) × 200dp.
  • wideView oferece suporte a 215 dp × 110 dp (minResizeHeight) a 250 dp (maxResizeWidth) × 200dp (maxResizeHeight).

O widget precisa ser compatível com o intervalo de tamanho de minResizeWidth × minResizeHeight a maxResizeWidth × maxResizeHeight. Dentro desse intervalo, decida o ponto de corte para mudar os layouts.

Exemplo de layout responsivo
Figura 1. Exemplo de layout responsivo.

Fornecer layouts exatos

Se não for viável usar um pequeno conjunto de layouts responsivos, é possível fornecer layouts diferentes de acordo com os tamanhos de exibição do widget. Normalmente, há dois tamanhos para smartphones (modo retrato e paisagem) e quatro para dispositivos dobráveis.

Para implementar essa solução, o app precisa realizar as seguintes etapas:

  1. Sobrecarregue AppWidgetProvider.onAppWidgetOptionsChanged(), que é chamado quando o conjunto de tamanhos muda.

  2. Chame AppWidgetManager.getAppWidgetOptions(), que retorna um Bundle contendo os tamanhos.

  3. Acesse a chave AppWidgetManager.OPTION_APPWIDGET_SIZES do Bundle.

O exemplo de código a seguir mostra como fornecer layouts exatos.

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) { }

Determinar um tamanho para o widget

Cada widget precisa definir um targetCellWidth e um targetCellHeight para dispositivos. com o Android 12 ou mais recente, ou minWidth e minHeight para todos versões do Android, indicando a quantidade mínima de espaço que consome por padrão. No entanto, quando os usuários adicionam um widget à tela inicial, geralmente ocupa mais do que a largura e a altura mínimas especificadas.

As telas iniciais do Android oferecem aos usuários uma grade de espaços disponíveis onde eles podem widgets e ícones de lugar. Essa grade pode variar de acordo com o dispositivo. por exemplo, muitas os celulares oferecem uma grade 5x4 e os tablets podem oferecer uma grade maior. Quando seu widget é adicionado, ele é esticado para ocupar o número mínimo de células, horizontal e verticalmente, necessária para satisfazer às restrições de seus targetCellWidth e targetCellHeight em dispositivos em execução Android 12 ou mais recente, ou restrições minWidth e minHeight no dispositivos com o Android 11 (nível 30 da API) ou versões anteriores.

A largura e a altura de uma célula e o tamanho das margens automáticas aplicadas aos widgets pode variar entre os dispositivos. Use a tabela a seguir para fazer uma estimativa aproximada as dimensões mínimas do widget em um celular comum de grade 5x4, dada a número de células de grade ocupadas que você deseja:

Número de células (largura x altura) Tamanho disponível no modo retrato (dp) Tamanho disponível no modo paisagem (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)

Use os tamanhos de célula no modo retrato para informar os valores fornecidos os atributos minWidth, minResizeWidth e maxResizeWidth. Da mesma forma, use os tamanhos de célula no modo paisagem para informar os valores fornecidos para os atributos minHeight, minResizeHeight e maxResizeHeight.

A razão para isso é que a largura da célula normalmente é menor no modo retrato. do que no modo paisagem – e, da mesma forma, a altura da célula é normalmente menores no modo paisagem do que no modo retrato.

Por exemplo, se você quiser que a largura do widget seja redimensionável para uma célula um Google Pixel 4, defina a minResizeWidth como 56 dp, no máximo. para garantir que o valor do atributo minResizeWidth seja menor que 57 dp, porque uma célula tem pelo menos 57 dp de largura no modo retrato. Da mesma forma, se você quiser que a altura do widget seja redimensionável em uma célula da mesmo dispositivo, defina minResizeHeight como no máximo 50 dp para garantir o valor do atributo minResizeHeight é menor que 51 dp, porque uma célula tem pelo menos 51 dp de altura no modo paisagem.

Cada widget pode ser redimensionado dentro dos intervalos de tamanho entre o minResizeWidth/minResizeHeight e maxResizeWidth/maxResizeHeight o que significa que ele precisa se adaptar a qualquer intervalo de tamanho entre eles.

Por exemplo, para definir o tamanho padrão do widget na veiculação, é possível defina os seguintes atributos:

<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

Isso significa que o tamanho padrão do widget é de células 3x2, conforme especificado pelo atributos targetCellWidth e targetCellHeight, ou 180×110 dp, conforme especificado por minWidth e minHeight para dispositivos em execução Android 11 ou anterior. No último caso, o tamanho nas células pode variam de acordo com o dispositivo.

Além disso, para definir os intervalos de tamanho suportados para seu widget, você pode definir o seguinte atributos:

<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

Conforme especificado pelos atributos anteriores, a largura do widget é pode ser redimensionado de 180 dp a 530 dp, e a altura, de 110 dp a 450 dp. O widget pode ser redimensionado de células 3x2 para 5x2, desde que o seguinte quando há condições:

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);

Suponha que o widget use os layouts responsivos definidos no modelo snippets de código. Isso significa que o layout especificado como R.layout.widget_weather_forecast_small é usado a partir de 180 dp (minResizeWidth) x 110 dp (minResizeHeight) a 269 x 279 dp (próximo ponto de corte - 1). Da mesma forma, R.layout.widget_weather_forecast_medium é usado de 270 x 110 dp a 270 x 279 dp; e R.layout.widget_weather_forecast_large é usado de 270x280dp para 530dp (maxResizeWidth) x 450dp (maxResizeHeight).

À medida que o usuário redimensiona o widget, sua aparência muda para se adaptar a cada tamanho células, conforme mostrado nos exemplos a seguir.

Exemplo de widget de clima no menor tamanho de grade 3x2. A interface mostra
            o nome do local (Tóquio), a temperatura (14°) e o símbolo que indica
            clima parcialmente nublado.
Figura 2. 3x2 R.layout.widget_weather_forecast_small.

Exemplo de widget de clima em um tamanho 4x2 &quot;médio&quot; tamanho. Redimensionar o widget
            com base em toda a IU do tamanho do widget anterior,
            e adiciona o rótulo &quot;Predominantemente nublado&quot;. e uma previsão de temperaturas a partir de
            Das 16h às 19h.
Figura 3. 4x2 R.layout.widget_weather_forecast_medium.

Exemplo de widget de clima em um tamanho 5x2 &quot;médio&quot; tamanho. Redimensionar o widget
            dessa forma resulta na mesma interface do tamanho anterior, exceto pelo fato de que
            esticada por uma célula para ocupar mais espaço horizontal.
Figura 4. 5x2 R.layout.widget_weather_forecast_medium.

Exemplo de widget do clima em um tamanho &quot;grande&quot; 5x3 tamanho. Redimensionar o widget
            com base em toda a IU dos tamanhos de widget anteriores,
            e adiciona uma visualização dentro do widget com a previsão do tempo
            às terças e quartas-feiras. Símbolos que indicam tempo ensolarado ou chuvoso
            e temperaturas máximas e mínimas todos os dias.
Figura 5. 5x3 R.layout.widget_weather_forecast_large.

Exemplo de widget do clima em um tamanho &quot;grande&quot; 5x4 tamanho. Redimensionar o widget
            com base em toda a IU dos tamanhos de widget anteriores,
            e adiciona quinta e sexta-feira (e seus símbolos correspondentes
            indicando o tipo de clima, bem como temperaturas altas e baixas
            para cada dia).
Figura 6. 5x4 R.layout.widget_weather_forecast_large.