Fornire layout flessibili per i widget

In questa pagina vengono descritti i perfezionamenti per le dimensioni dei widget e una maggiore flessibilità introdotti in Android 12 (livello API 31). Descrive inoltre come determinare le dimensioni del widget.

Usa API migliorate per dimensioni e layout dei widget

A partire da Android 12 (livello API 31), puoi fornire attributi delle dimensioni più raffinati e layout flessibili seguendo questa procedura, come descritto nelle sezioni che seguono:

  1. Specifica vincoli aggiuntivi per le dimensioni del widget.

  2. Fornire layout adattabili o layout.

Nelle versioni precedenti di Android, è possibile ottenere gli intervalli di dimensioni di un utilizzando OPTION_APPWIDGET_MIN_WIDTH, OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH, e OPTION_APPWIDGET_MAX_HEIGHT e poi stimare le dimensioni del widget, ma questa logica non funziona in situazioni diverse. Per i widget destinati ad Android 12 o versioni successive, consigliamo di fornire layout adattabili o esatti.

Specifica vincoli aggiuntivi relativi alle dimensioni dei widget

Android 12 aggiunge API per garantire che il widget sia le dimensioni in modo più affidabile sui vari dispositivi con schermi di varie dimensioni.

Oltre agli attributi esistenti minWidth, minHeight, minResizeWidth, e minResizeHeight , utilizza i seguenti nuovi attributi appwidget-provider:

  • targetCellWidth e targetCellHeight: definiscono le dimensioni di destinazione del widget in termini di celle della griglia di avvio. Se definiti, questi attributi vengono utilizzati al posto di minWidth o minHeight.

  • maxResizeWidth e maxResizeHeight: definiscono le dimensioni massime a cui il programma di avvio consente all'utente di ridimensionare il widget.

Il seguente XML mostra come utilizzare gli attributi di taglia.

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

Fornire layout adattabili

Se il layout deve cambiare a seconda delle dimensioni del widget, ti consigliamo di creando un piccolo insieme di layout, ciascuno valido per un intervallo di dimensioni. Se non è possibile, un'altra opzione è fornire layout in base alle dimensioni esatte del widget in fase di esecuzione, come descritto in questa pagina.

Questa funzionalità consente una scalabilità più fluida e un sistema complessivamente migliore l'integrità perché il sistema non deve riattivare l'app ogni volta il widget viene visualizzato in una dimensione diversa.

Il seguente esempio di codice mostra come fornire un elenco di layout.

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

Supponiamo che il widget abbia i seguenti attributi:

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

Lo snippet di codice precedente indica quanto segue:

  • smallView supporta da 160 dp (minResizeWidth) × 110 dp (minResizeHeight) a 160 dp × 199 dp (punto di taglio successivo - 1 dp).
  • tallView supporta da 160 dp × 200 dp a 214 dp (punto di interruzione successivo - 1) × 200dp.
  • wideView supporta da 215 dp × 110 dp (minResizeHeight) a 250 dp (maxResizeWidth) × 200dp (maxResizeHeight).

Il widget deve supportare l'intervallo di dimensioni da minResizeWidth × minResizeHeight a maxResizeWidth × maxResizeHeight. All'interno di questo intervallo, puoi decidere il punto di interruzione per cambiare layout.

Esempio di layout adattabile
Figura 1. Esempio di layout adattabile.

Fornire layout esatti

Se un piccolo insieme di layout adattabili non è fattibile, puoi fornire layout diversi, in base alle dimensioni del widget. Questo è in genere due dimensioni per gli smartphone (modalità verticale e orizzontale) e quattro dimensioni per pieghevoli.

Per implementare questa soluzione, la tua app deve eseguire i seguenti passaggi:

  1. Sovraccarico AppWidgetProvider.onAppWidgetOptionsChanged(), che viene chiamato quando l'insieme di dimensioni cambia.

  2. Chiama AppWidgetManager.getAppWidgetOptions(), che restituisce un Bundle contenente le dimensioni.

  3. Accedi alla chiave AppWidgetManager.OPTION_APPWIDGET_SIZES dal Bundle.

Il seguente esempio di codice mostra come fornire layout esatti.

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

Determinare le dimensioni del widget

Ogni widget deve definire un targetCellWidth e un targetCellHeight per i dispositivi con Android 12 o versioni successive oppure minWidth e minHeight per tutte le versioni di Android, indicando la quantità minima di spazio che occupa per impostazione predefinita. Tuttavia, quando gli utenti aggiungono un widget alla schermata Home, in genere occupa più della larghezza e dell'altezza minime specificate.

Le schermate Home di Android offrono agli utenti una griglia di spazi disponibili in cui widget e icone dei luoghi. Questa griglia può variare in base al dispositivo. ad esempio molti gli smartphone offrono una griglia 5x4, mentre i tablet possono offrire una griglia più grande. Quando il widget viene aggiunto, viene allungato per occupare il numero minimo di celle, orizzontalmente e verticalmente, necessarie per soddisfare i vincoli per targetCellWidth e targetCellHeight sui dispositivi con Android 12 o versioni successive oppure i vincoli per minWidth e minHeight sui dispositivi con Android 11 (livello API 30) o versioni precedenti.

Sia la larghezza che l'altezza di una cella e le dimensioni dei margini automatici applicati ai widget possono variare in base al dispositivo. Usa la seguente tabella per fare una stima approssimativa le dimensioni minime del widget in un tipico smartphone con griglia 5 x 4, date le numero di celle della griglia occupate desiderato:

Numero di celle (larghezza x altezza) Dimensioni disponibili in modalità Ritratto (dp) Dimensioni disponibili in modalità Orizzontale (dp)
1x1 57 x 102 dp 127x51dp
2x1 130 x 102 dp 269 x 51 dp
3x1 203x102dp 412 x 51 dp
4x1 276x102dp 554 x 51 dp
5 x 1 349x102dp 697 x 51 dp
5x2 349 x 220 dp 697 x 117 dp
5x3 349 x 337 dp 697 x 184 dp
5x4 349 x 455 dp 697 x 250 dp
...
n x m (73n - 16) x (118m - 16) (142n - 15) x (66m - 15)

Utilizza le dimensioni delle celle in modalità Ritratto per specificare i valori da fornire per gli attributi minWidth, minResizeWidth e maxResizeWidth. Analogamente, utilizza le dimensioni delle celle in modalità Orizzontale per definire i valori che fornisci per gli attributi minHeight, minResizeHeight e maxResizeHeight.

Il motivo è che la larghezza della cella è in genere inferiore in modalità verticale rispetto alla modalità Orizzontale e, analogamente, l'altezza della cella è solitamente in modalità orizzontale rispetto a quella verticale.

Ad esempio, se vuoi che la larghezza del widget sia ridimensionabile fino a una cella Google Pixel 4, devi impostare minResizeWidth al massimo a 56 dp per assicurarti che il valore dell'attributo minResizeWidth sia inferiore di almeno 57 dp, perché una cella ha una larghezza di almeno 57 dp in verticale. Allo stesso modo, se vuoi che l'altezza del widget sia ridimensionabile in una cella della stesso dispositivo, devi impostare minResizeHeight al massimo a 50 dp per assicurarti il valore dell'attributo minResizeHeight è inferiore a 51 dp, perché una cella ha un'altezza di almeno 51 dp in modalità Orizzontale.

Ogni widget è ridimensionabile entro gli intervalli di dimensioni compresi tra minResizeWidth/minResizeHeight e maxResizeWidth/maxResizeHeight , il che significa che deve adattarsi a qualsiasi intervallo di dimensioni tra di loro.

Ad esempio, per impostare la dimensione predefinita del widget nel posizionamento, puoi imposta i seguenti attributi:

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

Ciò significa che le dimensioni predefinite del widget sono celle 3 x 2, come specificato dagli attributi targetCellWidth e targetCellHeight, oppure 180 x 110 dp, come specificato da minWidth e minHeight per i dispositivi con Android 11 o versioni precedenti. Nel secondo caso, la dimensione delle celle può variano in base al dispositivo.

Inoltre, per impostare gli intervalli di dimensioni supportati dal widget, puoi impostare quanto segue attributi:

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

Come specificato dagli attributi precedenti, la larghezza del widget è ridimensionabile da 180 dp a 530 dp e la sua altezza è ridimensionabile da 110 dp a 450 dp. Il widget può quindi essere ridimensionato da celle 3x2 a 5x2, a condizione che siano presenti le seguenti condizioni:

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

Supponiamo che il widget utilizzi i layout adattabili definiti negli snippet di codice precedenti. Ciò significa che il layout specificato R.layout.widget_weather_forecast_small utilizzata da 180 dp (minResizeWidth) x Da 110 dp (minResizeHeight) a 269 x 279 dp (punti di interruzione successivi - 1). Analogamente,R.layout.widget_weather_forecast_medium viene utilizzato da 270 x 110 dp a 270 x 279 dp, mentreR.layout.widget_weather_forecast_large viene utilizzato da 270 x 280 dp a 530 dp (maxResizeWidth) x 450 dp (maxResizeHeight).

Man mano che l'utente ridimensiona il widget, il suo aspetto cambia per adattarsi a ogni dimensione in come mostrato nei seguenti esempi.

Esempio di widget meteo nelle dimensioni più piccole della griglia 3x2. L&#39;interfaccia utente mostra
            il nome della località (Tokyo), la temperatura (14°) e il simbolo che indica
            il meteo parzialmente nuvoloso.
Figura 2. 3x2 R.layout.widget_weather_forecast_small.

Esempio di widget meteo nelle dimensioni &quot;medie&quot; 4 x 2. Ridimensionamento del widget
            in questo modo si basa su tutta la UI
della precedente dimensione del widget,
            e aggiunge l&#39;etichetta &quot;Per lo più nuvoloso&quot; e una previsione delle temperature da
            Dalle 16:00 alle 19:00.
Figura 3. 4x2 R.layout.widget_weather_forecast_medium.

Esempio di widget meteo nelle dimensioni &quot;medie&quot; 5 x 2. Ridimensionamento del widget
            in questo modo si ottiene la stessa UI della dimensione precedente, ad eccezione del fatto che
            allungato di una cella per occupare più spazio orizzontale.
Figura 4. 5 x 2 R.layout.widget_weather_forecast_medium.

Esempio di widget meteo nelle dimensioni &quot;grandi&quot; 5 x 3. Ridimensionamento del widget
            si basa su tutta la UI
delle dimensioni precedenti dei widget,
            e aggiunge una vista all&#39;interno del widget contenente una previsione del meteo
            il martedì e il mercoledì. Simboli che indicano tempo soleggiato o piovoso
            e temperature massime e basse per ogni giorno.
Figura 5. 5 x 3 R.layout.widget_weather_forecast_large.

Esempio di widget meteo in formato 5 x 4 &quot;large&quot; dimensioni. Se ridimensioni il widget in questo modo, viene utilizzata tutta l&#39;interfaccia utente delle dimensioni precedenti dei widget e vengono aggiunti giovedì e venerdì (e i relativi simboli che indicano il tipo di tempo, nonché le temperature massime e minime per ogni giorno).
Figura 6. 5x4 R.layout.widget_weather_forecast_large.