W sekcjach poniżej opisujemy, jak utworzyć podstawowy widżet aplikacji za pomocą Glance.
Zadeklaruj AppWidget w pliku manifestu
Po wykonaniu czynności konfiguracyjnych zadeklaruj w aplikacji AppWidget i jego metadane.
Rozszerz odbiornik
AppWidgetzGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
Zarejestruj dostawcę widżetu aplikacji w pliku
AndroidManifest.xmli powiązanym pliku metadanych:<receiver android:name=".glance.MyReceiver" android:exported="true"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_app_widget_info" /> </receiver>
Dodawanie metadanych AppWidgetProviderInfo
Następnie postępuj zgodnie z instrukcjami w artykule Tworzenie widżetu, aby utworzyć i zdefiniować informacje o widżecie aplikacji w pliku @xml/my_app_widget_info.
Jedyna różnica w przypadku funkcji Glance polega na tym, że nie ma pliku XML initialLayout, ale musisz go zdefiniować. Możesz użyć wstępnie zdefiniowanego układu wczytywania dostępnego w bibliotece:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
Deklarowanie pliku XML AppWidgetProviderInfo
Obiekt AppWidgetProviderInfo definiuje podstawowe właściwości widżetu. Zdefiniuj AppWidgetProviderInfo w pliku zasobów metadanych XML (res/xml/my_app_widget_info.xml) wewnątrz elementu <appwidget-provider>:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Atrybuty rozmiaru widżetu
Domyślny ekran główny umieszcza widżety w oknie na podstawie siatki komórek o określonej wysokości i szerokości. Większość ekranów głównych pozwala widżetom przyjmować rozmiary, które są całkowitymi wielokrotnościami komórek siatki, np. 2 komórki w poziomie i 3 komórki w pionie.
Atrybuty rozmiaru widżetu pozwalają określić domyślny rozmiar widżetu oraz dolną i górną granicę jego rozmiaru. W tym kontekście domyślny rozmiar widżetu to rozmiar, który widżet przyjmuje po pierwszym dodaniu do ekranu głównego.
W tabeli poniżej opisano atrybuty <appwidget-provider> dotyczące rozmiaru widżetu:
| Atrybuty i opis | |
|---|---|
targetCellWidth i targetCellHeight (Android 12), minWidth i minHeight |
targetCellWidth i targetCellHeight oraz minWidth i minHeight – aby aplikacja mogła używać atrybutów minWidth i minHeight, jeśli urządzenie użytkownika nie obsługuje atrybutów targetCellWidth i targetCellHeight. Jeśli są obsługiwane, atrybuty targetCellWidth i targetCellHeight mają pierwszeństwo przed atrybutami minWidth i minHeight.
|
minResizeWidth i minResizeHeight |
Określ bezwzględny minimalny rozmiar widżetu. Te wartości określają rozmiar, poniżej którego widżet jest nieczytelny lub w inny sposób bezużyteczny. Użycie tych atrybutów pozwala użytkownikowi zmienić rozmiar widżetu na mniejszy niż domyślny. Atrybut minResizeWidth jest ignorowany, jeśli jest większy niż minWidth lub jeśli nie jest włączona zmiana rozmiaru w poziomie. Zobacz resizeMode. Podobnie atrybut minResizeHeight jest ignorowany, jeśli jest większy niż minHeight lub jeśli zmiana rozmiaru w pionie nie jest włączona. |
maxResizeWidth i maxResizeHeight |
Określ zalecany maksymalny rozmiar widżetu. Jeśli wartości nie są wielokrotnością wymiarów komórki siatki, są zaokrąglane w górę do najbliższego rozmiaru komórki. Atrybut maxResizeWidth jest ignorowany, jeśli jest mniejszy niż minWidth lub jeśli nie jest włączona zmiana rozmiaru w poziomie. Zobacz: resizeMode. Podobnie atrybut maxResizeHeight jest ignorowany, jeśli jest mniejszy niż minHeight lub jeśli zmiana rozmiaru w pionie nie jest włączona.
Funkcja wprowadzona w Androidzie 12. |
resizeMode |
Określa reguły, według których można zmieniać rozmiar widżetu. Możesz użyć tego atrybutu, aby umożliwić zmianę rozmiaru widżetów na ekranie głównym w poziomie, w pionie lub w obu osiach. Użytkownicy naciskają i przytrzymują widżet, aby wyświetlić uchwyty zmiany rozmiaru, a następnie przeciągają uchwyty poziome lub pionowe, aby zmienić jego rozmiar w układzie siatki. Wartości atrybutu resizeMode to horizontal, vertical i none. Aby zadeklarować, że rozmiar widżetu można zmieniać w poziomie i w pionie, użyj horizontal|vertical. |
Przykład
Aby zilustrować, jak atrybuty z powyższej tabeli wpływają na rozmiar widżetu, przyjmijmy te specyfikacje:
- Komórka siatki ma 30 dp szerokości i 50 dp wysokości.
- Dostępna jest ta specyfikacja atrybutu:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
Od Androida 12:
Użyj atrybutów targetCellWidth i targetCellHeight jako domyślnego rozmiaru widżetu.
Domyślny rozmiar widżetu to 2 x 2. Rozmiar widżetu można zmniejszyć do 2 x 1 lub zwiększyć do 4 x 3.
Android 11 lub starszy:
Użyj atrybutów minWidth i minHeight, aby obliczyć domyślny rozmiar widżetu.
Domyślna szerokość = Math.ceil(80 / 30) = 3
Domyślna wysokość = Math.ceil(80 / 50) = 2
Domyślny rozmiar widżetu to 3 x 2. Rozmiar widżetu można zmniejszyć do 2 x 1 lub zwiększyć do pełnego ekranu.
Dodatkowe atrybuty widżetu
W tabeli poniżej opisano atrybuty <appwidget-provider> dotyczące właściwości innych niż rozmiar widżetu.
| Atrybuty i opis | |
|---|---|
updatePeriodMillis |
Określa, jak często platforma widżetów żąda aktualizacji od GlanceAppWidgetReceiver, wywołując metodę wywołania zwrotnego onUpdate(). Zalecamy jak najrzadsze aktualizowanie – nie częściej niż raz na godzinę – aby oszczędzać baterię.
Szczegółowe informacje znajdziesz w sekcji Kiedy aktualizować widżety w artykule o zarządzaniu stanem Glance. |
initialLayout |
Wskazuje zasób layoutu, który definiuje układ wczytywania widżetu przed wyrenderowaniem kompozycji interfejsu Glance. Możesz użyć wstępnie zdefiniowanego układu wczytywania dostępnego w bibliotece: @layout/glance_default_loading_layout. |
configure |
Określa aktywność konfiguracji, która uruchamia się, gdy użytkownik dodaje widżet. Zapoznaj się z przewodnikiem Umożliwianie użytkownikom konfigurowania widżetów aplikacji. |
description |
Określa opis widżetu, który ma się wyświetlać w selektorze widżetów. Wprowadzono w Androidzie 12. |
previewLayout (Android 12) i previewImage (Android 11 i starsze) |
|
autoAdvanceViewId |
Określa identyfikator widoku podrzędnego widżetu, który jest automatycznie przełączany przez hosta widżetu. |
widgetCategory |
Określa, czy widżet może być wyświetlany na ekranie głównym (home_screen), ekranie blokady (keyguard) czy na obu tych ekranach. W przypadku Androida 5.0 lub nowszego prawidłowa jest tylko wartość home_screen. |
widgetFeatures |
Deklaruje funkcje obsługiwane przez widżet. Jeśli na przykład konfiguracja widżetu jest opcjonalna, określ zarówno configuration_optional, jak i reconfigurable. |
Zdefiniuj GlanceAppWidget
Utwórz nową klasę, która rozszerza klasę
GlanceAppWidgeti zastępuje metodęprovideGlance. To jest metoda, za pomocą której możesz wczytać dane potrzebne do renderowania widżetu:class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // In this method, load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here Text("Hello World") } } }
Utwórz instancję w pliku
glanceAppWidgetna urządzeniuGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
Masz teraz skonfigurowany AppWidget za pomocą funkcji Glance.
Zarządzanie pamięcią obrazów widżetów w różnych przedziałach rozmiarów
Gdy widżet wyświetla bitmapy lub identyfikatory URI obrazów content://, pamiętaj, aby łączna ilość pamięci na zserializowane obrazy RemoteViews nie przekraczała limitu systemowego (6 * width *
height b, wymuszanego przez IllegalArgumentException na Androidzie 17 i nowszych). Aby to zrobić:
- Dekodowanie udostępnionych bitmap przed
provideContent: jeśli widżet używaSizeMode.ResponsivelubSizeMode.Exact, Glance uruchamia funkcję lambdaprovideContentraz dla każdego zasobnikaDpSize. DekodowanieBitmapwewnątrzprovideContentprzydziela oddzielną instancjęBitmapdla każdego zasobnika, co zwiększa wykorzystanie pamięci wRemoteViews.mBitmapCache. Dekoduj bitmapy o zmniejszonym próbkowaniu (Bitmap.Config.RGB_565w przypadku obrazów nieprzezroczystych) raz wprovideGlanceprzedprovideContent, aby wszystkie zasobniki korzystały z jednego odwołaniaBitmap. Użyj odpowiedniego importu
ImageProvider: Jetpack Glance udostępnia 2 różne przeciążeniaImageProviderw 2 pakietach:- Użyj
import androidx.glance.ImageProvider, gdy przekazujesz zasóbImageProvider(bitmap: Bitmap)w pamięci lub obiekt rysowalnyImageProvider(resId: Int). - Podczas przekazywania identyfikatora URI używaj
import androidx.glance.appwidget.ImageProviderImageProvider(uri: Uri). Podczas udostępniania plików z pamięci podręcznej za pomocą identyfikatora URIFileProviderprzyznaj uprawnienieIntent.FLAG_GRANT_READ_URI_PERMISSIONkażdemu zainstalowanemu pakietowi programu uruchamiającegoIntent.CATEGORY_HOME, aby host mógł wyrenderować obraz.
- Użyj
Obsługa komunikatów widżetu za pomocą klasy GlanceAppWidgetReceiver
Widżet współrzędnych GlanceAppWidgetReceiver transmituje i aktualizuje stan platformy, rozszerzając bazowy element AppWidgetProvider. Odbiera zdarzenia platformy, gdy widżet jest aktualizowany, usuwany, włączany lub wyłączany, i przekształca je w żądania cyklu życia Compose.
Deklarowanie widżetu w pliku manifestu
Zadeklaruj podklasę klasy GlanceAppWidgetReceiver jako odbiornik komunikatów w pliku AndroidManifest.xml:
<receiver android:name="MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info" />
</receiver>
Element <receiver> wymaga atrybutu android:name, który określa klasę odbiornika. Odbiorca musi zaakceptować działanie rozgłaszania ACTION_APPWIDGET_UPDATE w <intent-filter>.
Element <meta-data> musi mieć nazwę android.appwidget.provider, a atrybut android:resource musi wskazywać na zasób metadanych XML AppWidgetProviderInfo (@xml/my_app_widget_info).
Implementowanie klasy GlanceAppWidgetReceiver
W Glance rozszerzasz GlanceAppWidgetReceiver, a nie bezpośrednio AppWidgetProvider. Zaimplementuj go, łącząc odbiornik z instancją GlanceAppWidget. Główne wywołania zwrotne dostępne w GlanceAppWidgetReceiver działają w następujący sposób:
onUpdate(): automatycznie zastępowany przez Glance w celu wykonywania aktualizacji kompozycji. Jeśli ręcznie zastąpiszonUpdate, musisz wywołaćsuper.onUpdate, aby umożliwić Glance uruchamianie wątków kompozycji.onAppWidgetOptionsChanged(): wywoływana, gdy widżet jest umieszczany po raz pierwszy lub gdy zmienia się jego rozmiar. Glance odczytuje elementy pakietu opcji, dzięki czemu układ płynnie dostosowuje się do wymiarów środowiska wykonawczego.onDeleted(Context, IntArray): wywoływana, gdy użytkownik usunie konkretną instancję widżetu.onEnabled(Context): wywoływane, gdy zostanie utworzone pierwsze wystąpienie widżetu. Doskonale nadaje się do przeprowadzania globalnych migracji.onDisabled(Context): wywoływana, gdy ostatnia aktywna instancja dostawcy zostanie usunięta.onReceive(Context, Intent): przechwytuje każdą transmisję platformy przed określonymi metodami wywołania zwrotnego. Musisz upewnić się, że każda niestandardowa logika odbiornika, którą napiszesz, wywołujesuper.onReceive(context, intent)i nigdy nie wywołujegoAsync, ponieważ Glance automatycznie kieruje pracę asynchronicznie.
Odbieranie intencji rozgłaszania widżetu
W tle GlanceAppWidgetReceiver filtruje i obsługuje te podstawowe intencje rozgłaszania widżetów platformy:
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
Tworzenie interfejsu
Poniższy fragment kodu pokazuje, jak utworzyć interfejs:
/* Import Glance Composables In the event there is a name clash with the Compose classes of the same name, you may rename the imports per https://kotlinlang.org/docs/packages.html#imports using the `as` keyword. import androidx.glance.Button import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.text.Text */ class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // Load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here MyContent() } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MyActivity>() ) Button( text = "Work", onClick = actionStartActivity<MyActivity>() ) } } } }
Powyższy przykładowy kod wykonuje takie operacje:
- W elemencie najwyższego poziomu
Columnelementy są umieszczane pionowo jeden po drugim. Columnrozszerza swój rozmiar, aby dopasować się do dostępnego miejsca (za pomocąGlanceModifier), i wyrównuje zawartość do góry (verticalAlignment) oraz wyśrodkowuje ją w poziomie (horizontalAlignment).- Treść elementu
Columnjest definiowana za pomocą funkcji lambda. Kolejność ma znaczenie.- Pierwszym elementem w
Columnjest komponentTextz dopełnieniem12.dp. - Drugi element to
Row, w którym elementy są umieszczone poziomo jeden po drugim, z dwoma elementamiButtonswyśrodkowanymi w poziomie (horizontalAlignment). Ostateczny wygląd zależy od dostępnego miejsca. Poniższy obraz pokazuje, jak może to wyglądać:
- Pierwszym elementem w
Możesz zmienić wartości wyrównania lub zastosować inne wartości modyfikatora (np. dopełnienie), aby zmienić położenie i rozmiar komponentów. Pełną listę komponentów, parametrów i dostępnych modyfikatorów dla każdej klasy znajdziesz w dokumentacji referencyjnej.
Zaimplementuj zaokrąglone rogi
Android 12 wprowadza parametry systemowe, które umożliwiają dynamiczne dostosowywanie promieni narożników widżetów aplikacji:
system_app_widget_background_radius: określa promień narożnika kontenera tła widżetu (nigdy nie większy niż 28 dp).- Promień wewnętrzny: aby zapobiec przycinaniu treści, oblicz proporcjonalny promień treści wewnętrznej na podstawie obrysu tła systemowego:
systemRadiusValue - widgetPadding
W Glance możesz dynamicznie stosować właściwości rozmiaru promienia narożnika w kompozycji za pomocą GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).
Aby zapewnić zgodność wsteczną na urządzeniach z Androidem 11 (poziom API 30) lub starszym, zaimplementuj atrybuty niestandardowe i zasoby zastępcze motywu niestandardowego:
/values/attrs.xml<resources> <attr name="backgroundRadius" format="dimension" /> </resources>/values/styles.xml<resources> <style name="MyWidgetTheme"> <item name="backgroundRadius">@dimen/my_background_radius_dimen</item> </style> </resources>/values-31/styles.xml<resources> <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight"> <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item> </style> </resources>/drawable/my_widget_background.xml<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="?attr/backgroundRadius" /> </shape>