Creare un widget avanzato

Questa pagina spiega le pratiche consigliate per la creazione di un widget più avanzato per offrire un'esperienza utente migliore.

Ottimizzazioni per l'aggiornamento dei contenuti del widget

L'aggiornamento dei contenuti dei widget può essere costoso dal punto di vista del calcolo. Per risparmiare batteria consumo, ottimizzare il tipo di aggiornamento, la frequenza e la tempistica.

Tipi di aggiornamenti dei widget

Esistono tre modi per aggiornare un widget: un aggiornamento completo, un aggiornamento parziale e nel caso del widget di raccolta, un aggiornamento dei dati. Ciascuno di loro ha diverse i costi e le ramificazioni di calcolo.

Di seguito viene descritto ogni tipo di aggiornamento e vengono forniti snippet di codice per ciascuno.

  • Aggiornamento completo: chiama AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) per aggiornare completamente il widget. Questo sostituisce il token fornito in precedenza RemoteViews con un nuovo RemoteViews. Si tratta dell'aggiornamento più costoso dal punto di vista del calcolo.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
    }
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1");
    remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2");
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
    
  • Aggiornamento parziale: chiamata AppWidgetManager.partiallyUpdateAppWidget: per aggiornare parti del widget. Il nuovo RemoteViews viene unito al fornito in precedenza (RemoteViews). Questo metodo viene ignorato se un widget non riceve almeno un aggiornamento completo fino al giorno updateAppWidget(int[], RemoteViews).

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
    }
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text");
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
    
  • Aggiornamento dei dati della raccolta: chiamata AppWidgetManager.notifyAppWidgetViewDataChanged: per invalidare i dati di una vista raccolta nel widget. Questo attiva RemoteViewsFactory.onDataSetChanged Nel frattempo, i dati precedenti vengono visualizzati nel widget. Puoi tranquillamente di eseguire attività costose in modo sincrono con questo metodo.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
    
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
    

Puoi chiamare questi metodi da qualsiasi punto dell'app, purché questa includa la lo stesso UID dell'ID utente AppWidgetProvider.

Determinare la frequenza di aggiornamento di un widget

I widget vengono aggiornati periodicamente a seconda del valore fornito per updatePeriodMillis . Il widget può aggiornarsi in risposta all'interazione dell'utente, aggiornamenti, o entrambi.

Aggiorna periodicamente

Puoi controllare la frequenza dell'aggiornamento periodico specificando un valore per AppWidgetProviderInfo.updatePeriodMillis nel file XML appwidget-provider. Ciascuna update attiva il metodo AppWidgetProvider.onUpdate(), che consente di puoi inserire il codice per aggiornare il widget. Tuttavia, prendi in considerazione le alternative gli aggiornamenti dei Broadcast receiver descritti in sezione seguente se il widget deve caricare i dati in modo asincrono o richiede più tempo non superino i 10 secondi perché, dopo 10 secondi, il sistema considera BroadcastReceiver in modo che non risponda.

updatePeriodMillis non supporta valori inferiori a 30 minuti. Tuttavia, se vuoi disabilitare gli aggiornamenti periodici, puoi specificare 0.

Puoi consentire agli utenti di regolare la frequenza degli aggiornamenti in una configurazione. Per Ad esempio, potrebbero volere che il codice di borsa si aggiorni ogni 15 minuti o solo 4 volte al giorno. In questo caso, imposta updatePeriodMillis su 0 e utilizza WorkManager.

Aggiornamento in risposta a un'interazione dell'utente

Ecco alcuni modi consigliati per aggiornare il widget in base all'interazione dell'utente:

  • Da un'attività dell'app: chiama direttamente AppWidgetManager.updateAppWidget in risposta a un'interazione dell'utente, ad esempio come tocco dell'utente.

  • Dalle interazioni remote, ad esempio una notifica o un widget di un'app: creare un PendingIntent, quindi aggiornare il widget dalla chiamata Activity, Broadcast o Service. Puoi scegliere la tua priorità. Per Ad esempio, se selezioni un valore Broadcast per PendingIntent, puoi scegliere una trasmissione in primo piano per dare Priorità BroadcastReceiver.

Aggiornamento in risposta a un evento di trasmissione

Un esempio di evento di trasmissione che richiede l'aggiornamento di un widget è quando l'utente scatta una foto. In questo caso, vorrai aggiornare il widget quando viene creata una nuova foto viene rilevata.

Puoi pianificare un job con JobScheduler e specificare una trasmissione come utilizzando il comando JobInfo.Builder.addTriggerContentUri .

Puoi anche registrare un BroadcastReceiver per la trasmissione, ad esempio: in ascolto ACTION_LOCALE_CHANGED Tuttavia, poiché questo consumo di risorse del dispositivo, è necessario con cautela e ascolta. solo alla trasmissione specifica. Con l'introduzione della trasmissione limitazioni in Android 7.0 (livello API 24) e Android 8.0 (livello API 26), le app non possono registrare implicitamente delle trasmissioni nei loro manifest, con alcune eccezioni.

Considerazioni sull'aggiornamento di un widget da un BroadcastReceiver

Se il widget viene aggiornato da un BroadcastReceiver, tra cui AppWidgetProvider, tieni presente le seguenti considerazioni relative alla durata e priorità di un aggiornamento del widget.

Durata dell'aggiornamento

Di regola, il sistema consente ai broadcast receiver, che di solito vengono eseguiti il thread principale, eseguilo per un massimo di 10 secondi prima di considerare non reattivo e l'attivazione di un messaggio di errore L'applicazione "Risposta in corso" (ANR). Se occorre più tempo aggiorna il widget, prendi in considerazione le seguenti alternative:

  • Pianifica un'attività utilizzando WorkManager.

  • Concedi più tempo al destinatario con la goAsync. In questo modo i ricevitori vengono eseguiti per 30 secondi.

Vedi Considerazioni sulla sicurezza e best practice pratiche per ulteriori informazioni informazioni.

Priorità dell'aggiornamento

Per impostazione predefinita, le trasmissioni, incluse quelle effettuate utilizzando AppWidgetProvider.onUpdate: vengono eseguiti come processi in background. Ciò significa le risorse di sistema sovraccaricate possono causare un ritardo nella chiamata della trasmissione destinatario. Per assegnare la priorità alla trasmissione, impostala come processo in primo piano.

Ad esempio, aggiungi Intent.FLAG_RECEIVER_FOREGROUND il flag Intent passato a PendingIntent.getBroadcast quando l'utente tocchi una determinata parte del widget.

Crea anteprime accurate che includono elementi dinamici

Figura 1: un'anteprima del widget che non mostra voci di elenco.

Questa sezione illustra l'approccio consigliato per visualizzare più elementi in l'anteprima di un widget con una raccolta , ovvero un widget che utilizza una ListView, GridView o StackView.

Se il widget utilizza una di queste viste, creando un'anteprima scalabile direttamente che fornisce il widget riduce la qualità quando nell'anteprima del widget non viene visualizzato alcun elemento. Ciò si verifica perché i dati delle visualizzazioni raccolta sono impostati in modo dinamico in fase di runtime e sono simili immagine mostrata nella figura 1.

Per visualizzare correttamente le anteprime dei widget con visualizzazioni raccolte nel widget, ti consigliamo di mantenere un file di layout separato designato solo per l'anteprima. Questo file di layout separato include il layout effettivo del widget e una vista raccolta segnaposto con oggetti falsi. Ad esempio, puoi simulare un ListView fornendo un segnaposto LinearLayout con diversi elenchi falsi elementi.

Per illustrare un esempio per un elemento 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 di anteprima quando fornisci l'attributo previewLayout di i metadati AppWidgetProviderInfo. Devi comunque specificare il layout effettivo del widget per l'attributo initialLayout e utilizza il layout effettivo del widget quando creando 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 false, poiché l'elenco sono oggetti TextView. È possibile più complessi per fornire elementi falsi se gli elementi sono layout complessi.

Considera un elemento dell'elenco definito in widget_list_item.xml e composto 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 elementi di elenco falsi, puoi includere il layout più volte, ma in questo modo fa sì che ogni elemento dell'elenco sia identico. Per fornire voci di elenco univoche: questi passaggi:

  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 ciascuno 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 falsi 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>