Tworzenie prostego widżetu

Wypróbuj Compose
Jetpack Compose to zalecany zestaw narzędzi interfejsu na Androida. Dowiedz się, jak tworzyć widżety za pomocą interfejsów API w stylu Compose.

Widżety aplikacji to miniaturowe widoki aplikacji, które możesz umieszczać w innych aplikacjach, np. na ekranie głównym, i otrzymywać okresowe aktualizacje. Te widoki są w interfejsie nazywane widgetami. Możesz opublikować widget za pomocą dostawcy widgetów aplikacji (lub dostawcy widgetów). Składnik aplikacji, który zawiera inne widżety, nazywa się hostem widżetów aplikacji (lub hostem widżetów). Ilustracja 1 przedstawia przykładowy widżet muzyczny:

Przykład widżetu muzycznego
Rysunek 1. Przykład widżetu muzycznego.

Z tego dokumentu dowiesz się, jak opublikować widżet za pomocą dostawcy widżetów. Więcej informacji o tworzeniu własnego AppWidgetHost do hostowania widżetów aplikacji znajdziesz w artykule Tworzenie hosta widżetów.

Informacje o projektowaniu widżetu znajdziesz w artykule Omówienie widżetów aplikacji.

Komponenty widżetu

Aby utworzyć widżet, potrzebujesz tych podstawowych komponentów:

Obiekt AppWidgetProviderInfo
Opisuje metadane widżetu, takie jak jego układ, częstotliwość aktualizacji i klasa AppWidgetProvider. AppWidgetProviderInfo jest zdefiniowany w XML, zgodnie z opisem w tym dokumencie.
AppWidgetProvider zajęcia
Określa podstawowe metody, które umożliwiają programowe korzystanie z widżetu. Dzięki niemu otrzymujesz transmisje, gdy widżet jest aktualizowany, włączany, wyłączany lub usuwany. Deklarujesz interfejs AppWidgetProvider w pliku manifestu, a następnie implementujesz go zgodnie z opisem w tym dokumencie.
Wyświetlanie układu
Określa początkowy układ widżetu. Układ jest zdefiniowany w XML, jak opisano w tym dokumencie.

Ilustracja 2 pokazuje, jak te komponenty pasują do ogólnego procesu przetwarzania widżetów aplikacji.

Proces przetwarzania widżetu aplikacji
Rysunek 2. Proces przetwarzania widżetu aplikacji.

Jeśli widżet aplikacji wymaga konfiguracji przez użytkownika, zaimplementuj aktywność konfiguracji widżetu aplikacji. Ta aktywność umożliwia użytkownikom modyfikowanie ustawień widżetów, np. strefy czasowej widżetu zegara.

Zalecamy też wprowadzenie tych ulepszeń: elastyczne układy widżetów, różne ulepszenia, zaawansowane widżety, widżety kolekcjitworzenie hosta widżetów.

Zadeklaruj plik XML AppWidgetProviderInfo

Obiekt AppWidgetProviderInfo określa podstawowe cechy widżetu. Zdefiniuj obiekt AppWidgetProviderInfo w pliku zasobu XML za pomocą pojedynczego elementu <appwidget-provider> i zapisz go w folderze res/xml/ projektu.

Pokazuje to poniższy przykład:

<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/example_loading_appwidget"
    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 swoim oknie na podstawie siatki komórek o określonej wysokości i szerokości. Większość ekranów głównych pozwala widżetom zajmować rozmiary będące wielokrotnościami komórek siatki, np. 2 komórki w poziomie i 3 w pionie.

Atrybuty rozmiaru widżetu umożliwiają określenie domyślnego rozmiaru widżetu oraz podanie dolnej i górnej granicy jego rozmiaru. W tym kontekście domyślny rozmiar widżetu to rozmiar, jaki widżet przyjmuje po pierwszym dodaniu go do ekranu głównego.

W tej tabeli opisujemy atrybuty <appwidget-provider> dotyczące rozmiaru widżetu:

Atrybuty i opis
targetCellWidthtargetCellHeight (Android 12),minWidthminHeight
  • Od Androida 12 atrybuty targetCellWidthtargetCellHeight określają domyślny rozmiar widżetu w komórkach siatki. Te atrybuty ignorowane w Androidzie 11 i starszych wersjach, a mogą być ignorowane, jeśli ekran główny nie obsługuje układu siatki.
  • Atrybuty minWidthminHeight określają domyślny rozmiar widżetu w dp. Jeśli wartości minimalnej szerokości lub wysokości widżetu nie pasują do wymiarów komórek, są zaokrąglane w górę do najbliższego rozmiaru komórki.
Zalecamy podanie obu zestawów atrybutów: targetCellWidthtargetCellHeight oraz minWidthminHeight, aby aplikacja mogła używać atrybutów minWidthminHeight, jeśli urządzenie użytkownika nie obsługuje atrybutów targetCellWidthtargetCellHeight. Jeśli są obsługiwane, atrybuty targetCellWidth i targetCellHeight mają pierwszeństwo przed atrybutami minWidth i minHeight.
minResizeWidth i minResizeHeight Określ bezwzględnie minimalny rozmiar widżetu. Te wartości określają rozmiar, poniżej którego widżet jest nieczytelny lub w inny sposób nieużyteczny. Użycie tych atrybutów umożliwia użytkownikowi zmianę rozmiaru 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 nie jest włączona zmiana rozmiaru w pionie.
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 większy niż minHeight lub jeśli nie jest włączona zmiana rozmiaru w pionie. Wprowadzono w Androidzie 12.
resizeMode Określa reguły, według których można zmieniać rozmiar widżetu. Za pomocą tego atrybutu możesz zmieniać rozmiar widżetów na ekranie głównym w pionie, w poziomie lub w obu kierunkach. Użytkownicy naciskają i przytrzymują widżet, aby wyświetlić uchwyty zmiany rozmiaru, a następnie przeciągają uchwyty poziome lub pionowe, aby zmienić rozmiar widżetu w siatce układu. Wartości atrybutu resizeMode to horizontal, verticalnone. Aby zadeklarować, że rozmiar widżetu można zmieniać w pionie i poziomie, użyj horizontal|vertical.

Przykład

Aby pokazać, jak atrybuty z tabeli powyżej wpływają na rozmiar widżetu, przyjmijmy te specyfikacje:

  • Komórka siatki ma 30 dp szerokości i 50 dp wysokości.
  • Podajemy specyfikację tego 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 targetCellWidthtargetCellHeight jako domyślnego rozmiaru widżetu.

Domyślny rozmiar widżetu to 2x2. Rozmiar widżetu można zmniejszyć do 2x1 lub zwiększyć do 4x3.

Android 11 i starsze:

Do obliczania domyślnego rozmiaru widżetu używaj atrybutów minWidthminHeight.

Domyślna szerokość = Math.ceil(80 / 30) = 3

Wysokość domyślna = Math.ceil(80 / 50) = 2

Domyślny rozmiar widżetu to 3x2. Rozmiar widżetu można zmniejszyć do 2x1 lub zwiększyć do pełnego ekranu.

Dodatkowe atrybuty widżetu

W tej tabeli opisujemy atrybuty <appwidget-provider> dotyczące cech innych niż rozmiar widżetu.

Atrybuty i opis
updatePeriodMillis Określa, jak często platforma widżetów wysyła żądanie aktualizacji do AppWidgetProvider, wywołując metodę zwrotną onUpdate(). Aktualizacja nie musi nastąpić dokładnie o podanej godzinie. Aby oszczędzać baterię, zalecamy aktualizowanie tak rzadko, jak to możliwe – nie częściej niż raz na godzinę. Pełną listę kwestii, które należy wziąć pod uwagę przy wyborze odpowiedniego okresu aktualizacji, znajdziesz w artykule Optymalizacje aktualizacji treści widżetu.
initialLayout Wskazuje zasób układu, który określa układ widżetu.
configure Określa aktywność, która jest uruchamiana, gdy użytkownik doda widżet, umożliwiając mu skonfigurowanie właściwości widżetu. Zobacz Umożliwianie użytkownikom konfigurowania widżetów. Od Androida 12 aplikacja może pominąć początkową konfigurację. Szczegółowe informacje znajdziesz w artykule Używanie domyślnej konfiguracji widżetu.
description Określa opis selektora widżetów, który ma się wyświetlać w przypadku Twojego widżetu. Wprowadzono w Androidzie 12.
previewLayout (Android 12) i previewImage (Android 11 i starsze)
  • Od Androida 12 atrybut previewLayout określa skalowalny podgląd, który jest dostarczany jako układ XML ustawiony na domyślny rozmiar widżetu. Najlepiej, aby plik XML układu określony jako ten atrybut był tym samym plikiem XML układu co rzeczywisty widżet z realistycznymi wartościami domyślnymi.
  • W Androidzie 11 i starszych wersjach atrybut previewImage określa podgląd widżetu po jego skonfigurowaniu, który użytkownik widzi podczas wybierania widżetu aplikacji. Jeśli nie podasz ikony, użytkownik zobaczy ikonę aplikacji w programie uruchamiającym. To pole odpowiada atrybutowi android:previewImage w elemencie <receiver> w pliku AndroidManifest.xml.
Uwaga: zalecamy podanie atrybutów previewImagepreviewLayout, aby w przypadku, gdy urządzenie użytkownika nie obsługuje atrybutu previewLayout, aplikacja mogła używać atrybutu previewImage. Więcej informacji znajdziesz w artykule Wsteczna zgodność ze skalowalnymi podglądami widżetów.
autoAdvanceViewId Określa identyfikator widoku podrzędnego widżetu, który jest automatycznie przewijany 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 i nowszych wersji obowiązuje tylko home_screen.
widgetFeatures Deklaruje funkcje obsługiwane przez widżet. Jeśli na przykład chcesz, aby widżet używał domyślnej konfiguracji, gdy użytkownik go doda, określ flagi configuration_optionalreconfigurable. Dzięki temu po dodaniu widżetu przez użytkownika nie będzie uruchamiana aktywność konfiguracyjna. Użytkownik może później ponownie skonfigurować widżet.

Używanie klasy AppWidgetProvider do obsługi transmisji widżetów

Klasa AppWidgetProvider obsługuje transmisje widżetów i aktualizuje widżet w odpowiedzi na zdarzenia cyklu życia widżetu. W sekcjach poniżej znajdziesz informacje o tym, jak zadeklarować AppWidgetProvider w pliku manifestu, a potem wdrożyć tę funkcję.

Deklarowanie widżetu w pliku manifestu

Najpierw zadeklaruj klasę AppWidgetProvider w pliku AndroidManifest.xml aplikacji, jak pokazano w tym przykładzie:

<receiver android:name="ExampleAppWidgetProvider"
                 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/example_appwidget_info" />
</receiver>

Element <receiver> wymaga atrybutu android:name, który określa AppWidgetProvider używany przez widżet. Komponent nie może być eksportowany, chyba że osobny proces musi wysyłać transmisję do Twojego AppWidgetProvider, co zwykle nie ma miejsca.

Element <intent-filter> musi zawierać element <action> z atrybutem android:name. Ten atrybut określa, że AppWidgetProvider akceptuje ACTION_APPWIDGET_UPDATE transmisję. Jest to jedyna transmisja, którą musisz wyraźnie zadeklarować. AppWidgetManager automatycznie wysyła wszystkie inne transmisje widżetów do AppWidgetProvider w razie potrzeby.

Element <meta-data> określa zasób AppWidgetProviderInfo i wymaga tych atrybutów:

  • android:name: określa nazwę metadanych. Użyj znaku android.appwidget.provider, aby zidentyfikować dane jako deskryptor AppWidgetProviderInfo.
  • android:resource: określa lokalizację zasobu AppWidgetProviderInfo.

Implementowanie klasy AppWidgetProvider

Klasa AppWidgetProvider rozszerza BroadcastReceiver jako klasa pomocnicza do obsługi transmisji widżetów. Otrzymuje tylko transmisje zdarzeń, które są istotne dla widżetu, np. gdy widżet jest aktualizowany, usuwany, włączany lub wyłączany. Gdy wystąpią te zdarzenia transmisji, wywoływane są te metody:AppWidgetProvider

onUpdate()
Ta funkcja jest wywoływana w celu aktualizowania widżetu w interwałach zdefiniowanych przez atrybut updatePeriodMillis w elemencie AppWidgetProviderInfo. Więcej informacji znajdziesz w tabeli opisującej dodatkowe atrybuty widżetu na tej stronie.
Ta metoda jest też wywoływana, gdy użytkownik dodaje widżet, więc wykonuje niezbędne czynności konfiguracyjne, takie jak definiowanie procedur obsługi zdarzeń dla obiektów View lub uruchamianie zadań wczytywania danych do wyświetlenia w widżecie. Jeśli jednak zadeklarujesz działanie konfiguracyjne bez flagi configuration_optional, ta metoda nie jest wywoływana, gdy użytkownik dodaje widżet, ale jest wywoływana w przypadku kolejnych aktualizacji. Za przeprowadzenie pierwszej aktualizacji po zakończeniu konfiguracji odpowiada działanie konfiguracji. Więcej informacji znajdziesz w artykule Umożliwianie użytkownikom konfigurowania widżetów aplikacji.
Najważniejszym wywołaniem zwrotnym jest onUpdate(). Więcej informacji znajdziesz w sekcji Obsługa zdarzeń za pomocą klasy onUpdate() na tej stronie
.
onAppWidgetOptionsChanged()

Jest ona wywoływana, gdy widżet jest umieszczany po raz pierwszy i za każdym razem, gdy jest zmieniany jego rozmiar. Użyj tego wywołania zwrotnego, aby wyświetlać lub ukrywać treści w zależności od zakresów rozmiarów widżetu. Zakresy rozmiarów, a od Androida 12 także listę możliwych rozmiarów, jakie może przyjąć instancja widżetu, możesz uzyskać, wywołując getAppWidgetOptions(). Zwraca ona obiekt Bundle, który zawiera te elementy:

onDeleted(Context, int[])

Jest ona wywoływana za każdym razem, gdy widżet zostanie usunięty z hosta widżetów.

onEnabled(Context)

Jest wywoływana, gdy instancja widżetu jest tworzona po raz pierwszy. Jeśli na przykład użytkownik doda 2 instancje widżetu, wywołanie nastąpi tylko za pierwszym razem. Jeśli musisz otworzyć nową bazę danych lub przeprowadzić inną konfigurację, która musi zostać wykonana tylko raz dla wszystkich instancji widżetu, to jest to dobre miejsce na to.

onDisabled(Context)

Ta metoda jest wywoływana, gdy ostatnia instancja widżetu zostanie usunięta z hosta widżetu. W tym miejscu możesz usunąć wszystkie zadania wykonane w onEnabled(Context), np. usunąć tymczasową bazę danych.

onReceive(Context, Intent)

Jest ona wywoływana w przypadku każdej transmisji i przed każdą z poprzednich metod wywołania zwrotnego. Zwykle nie musisz implementować tej metody, ponieważ domyślna implementacja AppWidgetProviderfiltruje wszystkie transmisje widżetów i w odpowiedni sposób wywołuje poprzednie metody.

Musisz zadeklarować implementację klasy AppWidgetProvider jako odbiornik transmisji za pomocą elementu <receiver> w pliku AndroidManifest. Więcej informacji znajdziesz w sekcji Deklarowanie widżetu w pliku manifestu na tej stronie.

Obsługa zdarzeń za pomocą klasy onUpdate()

Najważniejszym wywołaniem zwrotnym AppWidgetProvider jest onUpdate(), ponieważ jest ono wywoływane, gdy każdy widżet jest dodawany do hosta, chyba że używasz aktywności konfiguracyjnej bez flagi configuration_optional. Jeśli widżet akceptuje zdarzenia interakcji użytkownika, zarejestruj w tym wywołaniu zwrotnym moduły obsługi zdarzeń. Jeśli widżet nie tworzy plików tymczasowych ani baz danych ani nie wykonuje innych działań, które wymagają czyszczenia, onUpdate() może być jedyną metodą wywołania zwrotnego, którą musisz zdefiniować.

Jeśli na przykład chcesz utworzyć widżet z przyciskiem, który po kliknięciu uruchamia aktywność, możesz użyć tej implementacji AppWidgetProvider:

Kotlin

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity.
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                    /* context = */ context,
                    /* requestCode = */  0,
                    /* intent = */ Intent(context, ExampleActivity::class.java),
                    /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            val views: RemoteViews = RemoteViews(
                    context.packageName,
                    R.layout.appwidget_provider_layout
            ).apply {
                setOnClickPendingIntent(R.id.button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

Java

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        for (int i=0; i < appWidgetIds.length; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                /* context = */ context,
                /* requestCode = */ 0,
                /* intent = */ intent,
                /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

Ten AppWidgetProvider definiuje tylko metodę onUpdate(), używając jej do utworzenia elementu PendingIntent, który uruchamia element Activity i dołącza go do przycisku widżetu za pomocą metody setOnClickPendingIntent(int, PendingIntent). Zawiera pętlę, która iteruje przez każdy wpis w appWidgetIds, czyli tablicy identyfikatorów, które identyfikują każdy widżet utworzony przez tego dostawcę. Jeśli użytkownik utworzy więcej niż 1 instancję widżetu, wszystkie będą aktualizowane jednocześnie. Jednak w przypadku wszystkich instancji widżetu zarządza się tylko 1 harmonogram updatePeriodMillis. Jeśli np. harmonogram aktualizacji jest ustawiony na co 2 godziny, a drugi egzemplarz widżetu zostanie dodany godzinę po pierwszym, oba będą aktualizowane w okresie zdefiniowanym przez pierwszy, a drugi okres aktualizacji zostanie zignorowany. Obie aktualizują się co 2 godziny, a nie co godzinę.

.

Więcej informacji znajdziesz w ExampleAppWidgetProvider.javaprzykładowej klasie.

Otrzymywanie intencji rozgłaszania widżetu

AppWidgetProvider to klasa ułatwiająca. Jeśli chcesz otrzymywać transmisje widżetu bezpośrednio, możesz zaimplementować własną funkcję BroadcastReceiver lub zastąpić wywołanie zwrotne onReceive(Context,Intent). Intencje, na które musisz zwrócić uwagę, to:

Tworzenie układu widżetu

Musisz zdefiniować początkowy układ widżetu w pliku XML i zapisać go w katalogu res/layout/ projektu. Szczegółowe informacje znajdziesz w wytycznych dotyczących projektowania.

Tworzenie układu widżetu jest proste, jeśli znasz układy. Pamiętaj jednak, że układy widżetów są oparte na RemoteViews, które nie obsługuje wszystkich rodzajów układów ani widżetów widoku. Nie możesz używać widoków niestandardowych ani podklas widoków obsługiwanych przez RemoteViews.

RemoteViews obsługuje też ViewStub, czyli niewidoczny element View o rozmiarze zero, którego możesz używać do leniwego powiększania zasobów układu w czasie działania.

Obsługa zachowań stanowych

W Androidzie 12 dodano obsługę zachowań stanowych przy użyciu tych komponentów:

Widżet nadal nie ma stanu. Aplikacja musi przechowywać stan i rejestrować zdarzenia zmiany stanu.

Przykład widżetu listy zakupów pokazujący zachowanie z zachowaniem stanu
Rysunek 3. Przykład zachowania stanowego.

Poniższy przykład kodu pokazuje, jak wdrożyć te komponenty.

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
        R.id.my_checkbox,
        RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));

Podaj 2 układy: jeden dla urządzeń z Androidem 12 lub nowszym w folderze res/layout-v31, a drugi dla urządzeń z Androidem 11 lub starszym w domyślnym folderze res/layout.

Implementowanie zaokrąglonych narożników

Android 12 wprowadza te parametry systemowe, które umożliwiają ustawianie promieni zaokrąglonych rogów widżetu:

  • system_app_widget_background_radius: promień zaokrąglenia tła widżetu, który nigdy nie jest większy niż 28 dp.

  • Promień wewnętrzny, który można obliczyć na podstawie promienia zewnętrznego i wypełnienia. Przyjrzyj się temu fragmentowi kodu:

    /**
     * Applies corner radius for views that are visually positioned [widgetPadding]dp inside of the
     * widget background.
     */
    @Composable
    fun GlanceModifier.appWidgetInnerCornerRadius(widgetPadding: Dp): GlanceModifier {
    
        if (Build.VERSION.SDK_INT < 31) {
            return this
        }
    
        val resources = LocalContext.current.resources
        // get dimension in float (without rounding).
        val px = resources.getDimension(android.R.dimen.system_app_widget_background_radius)
        val widgetBackgroundRadiusDpValue = px / resources.displayMetrics.density
        if (widgetBackgroundRadiusDpValue < widgetPadding.value) {
            return this
        }
        return this.cornerRadius(Dp(widgetBackgroundRadiusDpValue - widgetPadding.value))
    }

Aby obliczyć odpowiedni promień wewnętrznej części widżetu, użyj tego wzoru: systemRadiusValue - widgetPadding

Widżety, które przycinają swoje treści do kształtów innych niż prostokątne, powinny używać identyfikatora widoku @android:id/background jako identyfikatora widoku tła, w którym parametr android:clipToOutline ma wartość true.

Ważne uwagi dotyczące zaokrąglonych rogów

  • Launchery innych firm i producenci urządzeń mogą zastąpić parametr system_app_widget_background_radius wartością mniejszą niż 28 dp.
  • Jeśli widżet nie używa @android:id/background ani nie definiuje tła, które przycina jego zawartość na podstawie konturu – z ustawieniem android:clipToOutline na true – program uruchamiający automatycznie identyfikuje tło i przycina widżet za pomocą prostokąta z zaokrąglonymi rogami ustawionego na promień systemowy.

  • Kształty inne niż prostokątne muszą mieścić się w zaokrąglonym prostokątnym kontenerze zmiany rozmiaru, aby nie zostały przycięte.

  • Od Androida 16 wartość systemowa AOSP dla parametru system_app_widget_background_radius to 24dp. Programy uruchamiające i producenci urządzeń mogą przycinać widżet do system_app_widget_background_radius.

  • Wewnętrzna zawartość widżetu musi mieć wystarczające dopełnienie, aby obsługiwać wartości promienia system_app_widget_background_radius do 28dp. Dzięki temu unikniesz przycinania treści przez zaokrąglone rogi.

Aby zapewnić zgodność widżetu z poprzednimi wersjami Androida, zalecamy zdefiniowanie atrybutów niestandardowych i użycie niestandardowego motywu do zastąpienia ich w Androidzie 12, jak pokazano w tych przykładowych plikach XML:

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

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />