Tworzenie zaawansowanego widżetu

Na tej stronie znajdziesz informacje o sprawdzonych metodach tworzenia bardziej zaawansowanego widżetu, który zapewni większą wygodę użytkownikom.

Optymalizacja aktualizacji treści widżetu

Aktualizowanie zawartości widżetu może być kosztowne pod względem obliczeń. Aby zmniejszyć zużycie baterii, zoptymalizuj typ, częstotliwość i czas aktualizacji.

Rodzaje aktualizacji widżetów

Widżet możesz zaktualizować na 3 sposoby: w pełni, częściowo lub, w przypadku widżetu kolekcji, odświeżyć dane. Każdy z nich wiąże się z różnymi kosztami i konsekwencjami obliczeniowymi.

Poniżej opisujemy poszczególne typy aktualizacji i zamieszczamy odpowiednie fragmenty kodu.

  • Pełna aktualizacja: wywołaj funkcję AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews), aby w pełni zaktualizować widżet. Spowoduje to zastąpienie wcześniej podanej wartości RemoteViews nowym elementem RemoteViews. To najbardziej czasochłonne z aktualizacji.

    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);
  • Częściowa aktualizacja: wywołaj AppWidgetManager.partiallyUpdateAppWidget, by zaktualizować części widżetu. Spowoduje to połączenie nowego RemoteViews z dostarczonym wcześniej RemoteViews. Ta metoda jest ignorowana, jeśli widżet nie otrzyma co najmniej 1 pełnego uaktualnienia za pomocą 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);
  • Odświeżanie danych kolekcji: wywołaj AppWidgetManager.notifyAppWidgetViewDataChanged, aby unieważnić dane widoku kolekcji w widżecie. Spowoduje to uruchomienie RemoteViewsFactory.onDataSetChanged. W międzyczasie w widżecie będą wyświetlane stare dane. Dzięki tej metodzie możesz bezpiecznie wykonywać kosztowne zadania w sposób synchroniczny.

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

Te metody możesz wywoływać z dowolnego miejsca w aplikacji, o ile ma ona ten sam identyfikator UID co odpowiadająca jej klasa AppWidgetProvider.

Określanie częstotliwości aktualizowania widżetu

Widgety są okresowo aktualizowane w zależności od wartości podanej dla atrybutu updatePeriodMillis. Widget może się aktualizować w reakcji na interakcję z użytkownikiem, aktualizacje rozsyłane do wielu urządzeń lub obie te czynności.

Aktualizuj okresowo

Częstotliwość okresowych aktualizacji możesz kontrolować, określając wartość elementu AppWidgetProviderInfo.updatePeriodMillis w pliku XML appwidget-provider. Każda aktualizacja uruchamia metodę AppWidgetProvider.onUpdate(), w której możesz umieścić kod aktualizujący widżet. Jeśli jednak widżet musi wczytywać dane asynchronicznie lub jego aktualizacja trwa dłużej niż 10 sekund, rozważ alternatywy dla aktualizacji odbiornika transmisji opisane w następującej sekcji, ponieważ po 10 sekundach system uzna, że BroadcastReceiver nie odpowiada.

updatePeriodMillis nie obsługuje wartości krótszych niż 30 minut. Jeśli jednak chcesz wyłączyć okresowe aktualizacje, możesz ustawić wartość 0.

Możesz zezwolić użytkownikom na dostosowywanie częstotliwości aktualizacji w konfiguracji. Mogą na przykład chcieć, aby ticker akcji był aktualizowany co 15 minut lub tylko 4 razy dziennie. W takim przypadku ustaw updatePeriodMillis na 0 i użyj WorkManager.

Aktualizacja w odpowiedzi na interakcję użytkownika

Oto kilka zalecanych sposobów aktualizowania widżetu na podstawie interakcji użytkownika:

  • Z poziomu aktywności w aplikacji: wywołaj bezpośrednio AppWidgetManager.updateAppWidget w odpowiedzi na interakcję użytkownika, np. kliknięcie.

  • Z interakcji zdalnych, takich jak powiadomienie lub widżet aplikacji: utwórz PendingIntent, a potem zaktualizuj widżet z wywołanych Activity, Broadcast lub Service. Możesz wybrać własny priorytet. Jeśli na przykład wybierzesz Broadcast dla PendingIntent, możesz wybrać komentarz na pierwszym planie, aby nadać BroadcastReceiver priorytet.

Aktualizacja w odpowiedzi na zdarzenie transmisji

Przykładem transmitowanego wydarzenia, które wymaga aktualizacji widżetu, jest zrobienie zdjęcia przez użytkownika. W tym przypadku chcesz zaktualizować widget, gdy zostanie wykryte nowe zdjęcie.

Możesz zaplanować zadanie za pomocą funkcji JobScheduler i określić transmisję jako aktywator przy użyciu metody JobInfo.Builder.addTriggerContentUri.

Możesz również zarejestrować adres BroadcastReceiver na potrzeby transmisji, na przykład nasłuchiwać ACTION_LOCALE_CHANGED. Ponieważ jednak zużywa to zasoby urządzenia, korzystaj z tej funkcji ostrożnie i nasłuchuj tylko konkretnego komunikatu. W Androidzie 7.0 (poziom interfejsu API 24) i Androidzie 8.0 (poziom interfejsu API 26) wprowadzono ograniczenia dotyczące transmisji danych, które uniemożliwiają rejestrowanie w plikach manifestu niejawnych transmisji danych (z pewnymi wyjątkami).

O czym należy pamiętać podczas aktualizowania widżetu z poziomu BroadcastReceiver

Jeśli widżet jest aktualizowany z poziomu BroadcastReceiver, w tym AppWidgetProvider, pamiętaj o tych kwestiach dotyczących czasu trwania i priorytetu aktualizacji widżetu.

Czas trwania aktualizacji

Zazwyczaj system pozwala odbiornikom transmisji, które zwykle działają w głównym wątku aplikacji, działać przez maksymalnie 10 sekund, zanim uzna je za niedziałające i wyświetli błąd Aplikacja nie odpowiada (ANR). Jeśli aktualizacja widżetu trwa dłużej, rozważ te opcje:

  • Zaplanuj zadanie za pomocą WorkManager.

  • Daj odbiorcy więcej czasu, korzystając z metody goAsync. Dzięki temu odbiorcy będą wykonywać 30 sekund.

Więcej informacji znajdziesz w artykule Uwagi na temat bezpieczeństwa i sprawdzone metody.

Priorytet aktualizacji

Domyślnie transmisje (w tym te realizowane za pomocąAppWidgetProvider.onUpdate) są wykonywane jako procesy w tle. Oznacza to, że przeciążone zasoby systemowe mogą powodować opóźnienie w wywoływaniu odbiornika. Aby nadawanie miało priorytet, ustaw je jako proces na pierwszym planie.

Możesz na przykład dodać do parametru Intent przekazywanego do parametru PendingIntent.getBroadcast flagę Intent.FLAG_RECEIVER_FOREGROUND, która jest wywoływana, gdy użytkownik kliknie określony element widżetu.

Tworzenie dokładnych podglądów, które zawierają elementy dynamiczne

Ilustracja 1. Podgląd widżetu, który nie wyświetla żadnych elementów listy.

W tej sekcji znajdziesz wyjaśnienie zalecanego podejścia do wyświetlania wielu elementów w podglądzie podglądu widżetu z widokiem kolekcji, czyli widżetu, który używa tagu ListView, GridView lub StackView.

Jeśli widżet używa jednego z tych widoków, tworzenie skalowanego podglądu przez bezpośrednie udostępnianie rzeczywistego układu widżetu pogarsza wrażenia użytkownika, gdy podgląd widżetu nie wyświetla żadnych elementów. Dzieje się tak, ponieważ dane widoku kolekcji są ustawiane dynamicznie w czasie wykonywania programu i wyglądają podobnie do obrazu przedstawionego na rysunku 1.

Aby podgląd widżetów z widokami kolekcji był prawidłowo wyświetlany w selektorze widżetów, zalecamy zachowanie osobnego pliku układu przeznaczonego tylko na podgląd. Ten oddzielny plik układu zawiera rzeczywisty układ widżetu i widok kolekcji obiektów zastępczych z fałszywymi elementami. Możesz na przykład zasymulować obiekt ListView, podając obiekt zastępczy LinearLayout z kilkoma fałszywymi elementami listy.

Aby zilustrować przykład ListView, zacznij od osobnego pliku układu:

// 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>

Podczas podawania atrybutu previewLayout w metadanych AppWidgetProviderInfo określ plik układu podglądu. Nadal określasz rzeczywisty układ widżetu dla atrybutu initialLayout i używasz rzeczywistego układu widżetu podczas tworzenia elementu RemoteViews w czasie wykonywania.

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

Elementy listy złożone

Przykład w poprzedniej sekcji zawiera fałszywe elementy listy, ponieważ są to obiekty TextView. Podanie fałszywych elementów może być trudniejsze, jeśli są to złożone układy.

Weź pod uwagę element listy zdefiniowany w elementach widget_list_item.xml, który składa się z 2 obiektów 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>

Aby podać fałszywe elementy listy, możesz użyć układu kilka razy, ale spowoduje to, że wszystkie elementy będą identyczne. Aby przesłać unikalne elementy listy, wykonaj te czynności:

  1. Utwórz zestaw atrybutów dla wartości tekstowych:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Aby ustawić tekst, użyj tych atrybutów:

    <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. Utwórz tyle stylów, ile jest potrzebnych do wyświetlenia podglądu. Zdefiniuj ponownie wartości w każdym stylu:

    <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. Zastosowanie stylów do fałszywych elementów w układzie podglądu:

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