Aggiungere le anteprime generate al selettore di widget

Le anteprime dei widget generati ti consentono di creare anteprime dinamiche e personalizzate per i tuoi widget che riflettono con precisione il modo in cui verranno visualizzati nella schermata Home di un utente. Vengono fornite tramite un'API push, il che significa che la tua app fornisce l'anteprima in qualsiasi momento del suo ciclo di vita senza ricevere una richiesta esplicita dall'host del widget.

Per migliorare l'esperienza di selezione dei widget della tua app, fornisci un'anteprima del widget generato sui dispositivi Android 15 e versioni successive, un'anteprima del widget scalata (specificando un previewLayout) per i dispositivi Android 12-14 e un previewImage per le versioni precedenti.

Per ulteriori informazioni, vedi Arricchisci la tua app con aggiornamenti live e widget su YouTube.

Configurare l'app per le anteprime dei widget generati

Per mostrare le anteprime dei widget generati su un dispositivo Android 15 o versioni successive, imposta prima il valore di compileSdk su 35 o versioni successive nel file del modulo build.gradle per avere la possibilità di fornire RemoteViews al selettore di widget

Le app possono quindi utilizzare setWidgetPreview in GlanceAppWidgetManager o AppWidgetManager. Per prevenire abusi e mitigare i problemi di integrità del sistema, setWidgetPreview è un'API con limitazione di frequenza. Il limite predefinito è di circa due chiamate all'ora.

Generare un'anteprima aggiornata con Jetpack Glance

Per i widget creati con Jetpack Glance:

  1. Esegui l'override della funzione GlanceAppWidget.providePreview per fornire i contenuti componibili per l'anteprima. Come faresti in provideGlance, carica i dati della tua app e passali al composable dei contenuti del widget per assicurarti che l'anteprima mostri dati accurati. A differenza di provideGlance, si tratta di una singola composizione senza ricomposizione o effetti.

  2. Chiama GlanceAppWidgetManager.setWidgetPreviews per generare e pubblicare l'anteprima.

Non è previsto un callback dal sistema per fornire anteprime, quindi la tua app deve decidere quando chiamare setWidgetPreviews. La strategia di aggiornamento dipende dal caso d'uso del widget:

  • Se il widget contiene informazioni statiche o è un'azione rapida, imposta l'anteprima al primo avvio dell'app.
  • Puoi impostare l'anteprima una volta che l'app ha dati, ad esempio dopo l'accesso di un utente o la configurazione iniziale.
  • Puoi configurare un'attività periodica per aggiornare le anteprime alla cadenza che preferisci.

Risoluzione dei problemi relativi alle anteprime generate

Un problema comune è che dopo aver generato un'anteprima, immagini, icone o altri composables potrebbero non essere presenti nell'immagine di anteprima rispetto alle dimensioni di rilascio del widget. Le dimensioni del calo sono definite da targetCellWidth e targetCellHeight, se specificati, oppure da minWidth e minHeight nel file di informazioni sul fornitore del widget app.

Ciò si verifica perché Android, per impostazione predefinita, esegue il rendering solo dei composable visibili alle dimensioni minime del widget. In altre parole, Android imposta previewSizeMode su SizeMode.Single per impostazione predefinita. Utilizza android:minHeight e android:minWidth nel file XML delle informazioni sul fornitore del widget dell'app per determinare quali composable disegnare.

Per risolvere il problema, sostituisci previewSizeMode in GlanceAppWidget e impostalo su SizeMode.Responsive, fornendo un insieme di valori DpSize. In questo modo Android conosce tutte le dimensioni del layout da eseguire il rendering per l'anteprima, il che garantisce che tutti gli elementi vengano visualizzati correttamente.

Ottimizza per fattori di forma specifici. Fornisci 1 o 2 dimensioni a partire da quella minima e seguendo i punti di interruzione del widget. Specifica almeno un'immagine per la compatibilità con le versioni precedenti. Puoi trovare i valori DP minimi appropriati per le diverse dimensioni della griglia nella guida alla progettazione dei widget.

Generare l'anteprima aggiornata senza Jetpack Glance

Puoi utilizzare RemoteViews senza Glance. L'esempio seguente carica una risorsa di layout del widget XML e la imposta come anteprima. Per visualizzare setWidgetPreview come metodo in questo snippet, è necessaria un'impostazione di build compileSdk pari a 35 o versioni successive.

AppWidgetManager.getInstance(appContext).setWidgetPreview(
    ComponentName(
        appContext,
        ExampleAppWidgetReceiver::class.java
    ),
    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
    RemoteViews("com.example", R.layout.widget_preview)
)

Aggiungere anteprime dei widget scalabili al selettore widget

A partire da Android 12, l'anteprima del widget visualizzata nel selettore dei widget è scalabile. Lo fornisci come layout XML impostato sulle dimensioni predefinite del widget. In precedenza, l'anteprima del widget era una risorsa disegnabile statica, in alcuni casi portava ad anteprime che riflettevano in modo impreciso l'aspetto dei widget quando vengono aggiunti alla schermata Home.

Per implementare anteprime dei widget scalabili, utilizza l'attributo previewLayout dell'elemento appwidget-provider per fornire invece un layout XML:

<appwidget-provider
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>

Ti consigliamo di utilizzare lo stesso layout del widget effettivo, con valori predefiniti o di test realistici. La maggior parte delle app utilizza gli stessi previewLayout e initialLayout. Per indicazioni sulla creazione di layout di anteprima accurati, consulta la sezione seguente di questa pagina.

Ti consigliamo di specificare sia gli attributi previewLayout che previewImage, in modo che la tua app possa utilizzare previewImage se il dispositivo dell'utente non supporta previewLayout. L'attributo previewLayout ha la precedenza sull'attributo previewImage.

Compatibilità con le versioni precedenti con le anteprime dei widget

Per consentire ai selettori di widget su Android 11 (livello API 30) o versioni precedenti di mostrare le anteprime del tuo widget o come fallback per le anteprime generate, specifica l'attributo previewImage.

Se modifichi l'aspetto del widget, aggiorna l'immagine di anteprima.

Creare anteprime accurate che includano elementi dinamici

Figura 1: un'anteprima del widget che non mostra elementi dell'elenco.

Questa sezione spiega l'approccio consigliato per visualizzare più elementi in un'anteprima del widget per un widget con una visualizzazione raccolta, ovvero un widget che utilizza ListView, GridView o StackView. Ciò non si applica alle anteprime dei widget generate.

Se il widget utilizza una di queste visualizzazioni, la creazione di un'anteprima scalabile fornendo direttamente il layout effettivo del widget peggiora l'esperienza quando l'anteprima del widget non mostra elementi. Ciò si verifica perché i dati della visualizzazione della raccolta vengono impostati dinamicamente in fase di runtime e sono simili all'immagine mostrata nella Figura 1.

Per visualizzare correttamente le anteprime dei widget con visualizzazioni raccolta nel selettore di widget, ti consigliamo di mantenere un file di layout separato designato solo per l'anteprima. Questo file di layout separato deve includere quanto segue:

  • Il layout effettivo del widget.
  • Una visualizzazione della raccolta segnaposto con elementi fittizi. Ad esempio, puoi imitare un ListView fornendo un segnaposto LinearLayout con diversi elementi di elenco fittizi.

Per illustrare un esempio per un ListView, inizia con un file di layout separato:

// res/layout/widget_preview.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="@drawable/widget_background"
   android:orientation="vertical">

    // Include the actual widget layout that contains ListView.
    <include
        layout="@layout/widget_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    // The number of fake items you include depends on the values you provide
    // for minHeight or targetCellHeight in the AppWidgetProviderInfo
    // definition.

    <TextView android:text="@string/fake_item1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

    <TextView android:text="@string/fake_item2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

</LinearLayout>

Specifica il file di layout dell'anteprima quando fornisci l'attributo previewLayout dei metadati AppWidgetProviderInfo. Continui a specificare il layout effettivo del widget per l'attributo initialLayout e a utilizzare il layout effettivo del widget quando costruisci un RemoteViews in fase di runtime.

<appwidget-provider
    previewLayout="@layout/widget_previe"
    initialLayout="@layout/widget_view" />

Voci di elenco complesse

L'esempio nella sezione precedente fornisce voci di elenco fittizie, perché le voci di elenco sono oggetti TextView. Può essere più complesso fornire elementi falsi se gli elementi sono layout complessi.

Considera una voce di elenco definita in widget_list_item.xml e composta da due oggetti TextView:

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <TextView android:id="@id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_title" />

    <TextView android:id="@id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_content" />
</LinearLayout>

Per fornire voci di elenco fittizie, puoi includere il layout più volte, ma in questo modo ogni voce di elenco sarà identica. Per fornire elementi di elenco unici, segui questa procedura:

  1. Crea un insieme di attributi per i valori di testo:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Utilizza questi attributi per impostare il testo:

    <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        <TextView android:id="@id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetTitle" />
    
        <TextView android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetContent" />
    </LinearLayout>
    
  3. Crea tutti gli stili necessari per l'anteprima. Ridefinisci i valori in ogni stile:

    <resources>
    
        <style name="Theme.Widget.ListItem">
            <item name="widgetTitle"></item>
            <item name="widgetContent"></item>
        </style>
        <style name="Theme.Widget.ListItem.Preview1">
            <item name="widgetTitle">Fake Title 1</item>
            <item name="widgetContent">Fake content 1</item>
        </style>
        <style name="Theme.Widget.ListItem.Preview2">
            <item name="widgetTitle">Fake title 2</item>
            <item name="widgetContent">Fake content 2</item>
        </style>
    
    </resources>
    
  4. Applica gli stili agli elementi fittizi nel layout di anteprima:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" ...>
    
        <include layout="@layout/widget_view" ... />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview1" />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview2" />
    
    </LinearLayout>