Stosowanie elastycznych układów widżetów

Na tej stronie opisujemy ulepszenia dotyczące rozmiaru widżetów i większej elastyczności wprowadzone w Androidzie 12 (poziom API 31). Znajdziesz tam też instrukcje dotyczące określania rozmiaru widżetu.

Używanie ulepszonych interfejsów API do obsługi rozmiarów i układów widżetów

Począwszy od Androida 12 (poziom interfejsu API 31) możesz podawać bardziej dokładne atrybuty rozmiarów i elastyczne układy, wykonując czynności opisane w następnych sekcjach:

  1. Określ dodatkowe ograniczenia rozmiaru widżetu.

  2. Za pomocą układów elastycznych lub ścisłych układy graficzne.

W poprzednich wersjach Androida można uzyskać zakresy rozmiarów jako za pomocą funkcji OPTION_APPWIDGET_MIN_WIDTH OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH, oraz OPTION_APPWIDGET_MAX_HEIGHT a potem oszacować rozmiar widżetu, ale to nie działa w różnych sytuacjach. W przypadku widżetów kierowanych na Androida 12 lub nowszego zalecamy użycie elastycznego układu lub dokładnego układu.

Określanie dodatkowych ograniczeń rozmiaru widżetu

Android 12 dodaje interfejsy API, dzięki którym możesz mieć pewność, że widżet w bardziej odpowiedni sposób na różnych urządzeniach o różnych rozmiarach ekranów.

Oprócz istniejących funkcji minWidth minHeight, minResizeWidth, oraz minResizeHeight użyj tych nowych atrybutów appwidget-provider:

  • targetCellWidthtargetCellHeight: określają docelowy rozmiar widżetu w komórkach siatki w wyszukiwarce. Jeśli te atrybuty są używane zamiast atrybutów minWidth i minHeight.

  • maxResizeWidth i maxResizeHeight: określić maksymalny rozmiar widżetu, na jaki pozwala program uruchamiający – jego rozmiar.

Poniższy kod XML pokazuje, jak korzystać z atrybutów dobierania rozmiaru.

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

Udostępnianie elastycznych układów

Jeśli układ musi się zmieniać w zależności od rozmiaru widgeta, zalecamy utworzenie niewielkiego zestawu układów, z których każdy będzie odpowiedni dla określonego zakresu rozmiarów. Jeśli nie jest możliwe, inną opcją jest określenie układów na podstawie dokładnie tego widżetu rozmiaru w czasie działania, jak opisano na tej stronie.

Ta funkcja zapewnia płynniejsze skalowanie i ogólne lepsze działanie systemu, ponieważ nie musi on budzić aplikacji za każdym razem, gdy wyświetla widżet w innej wielkości.

Poniższy przykładowy kod pokazuje, jak udostępnić listę układów.

Kotlin

override fun onUpdate(...) {
    val smallView = ...
    val tallView = ...
    val wideView = ...

    val viewMapping: Map<SizeF, RemoteViews> = mapOf(
            SizeF(150f, 100f) to smallView,
            SizeF(150f, 200f) to tallView,
            SizeF(215f, 100f) to wideView
    )
    val remoteViews = RemoteViews(viewMapping)

    appWidgetManager.updateAppWidget(id, remoteViews)
}

Java

@Override
public void onUpdate(...) {
    RemoteViews smallView = ...;
    RemoteViews tallView = ...;
    RemoteViews wideView = ...;

    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    viewMapping.put(new SizeF(150f, 100f), smallView);
    viewMapping.put(new SizeF(150f, 200f), tallView);
    viewMapping.put(new SizeF(215f, 100f), wideView);
    RemoteViews remoteViews = new RemoteViews(viewMapping);

    appWidgetManager.updateAppWidget(id, remoteViews);
}

Załóżmy, że widżet ma te atrybuty:

<appwidget-provider
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>

Fragment kodu oznacza, że:

  • smallView obsługuje formaty od 160 dp (minResizeWidth) × 110 dp (minResizeHeight) do 160 dp × 199 dp (następny punkt graniczny – 1 dp).
  • tallView obsługuje wartości od 160 x 200 dp do 214 dp (następny punkt graniczny – 1) x 200 dp.
  • wideView obsługuje wymiary od 215 dp × 110 dp (minResizeHeight) do 250 dp (maxResizeWidth) × 200dp (maxResizeHeight).

Widżet musi obsługiwać zakres rozmiarów od minResizeWidth × minResizeHeight do maxResizeWidth × maxResizeHeight. W tym zakresie możesz określić punkt graniczny, w którym nastąpi przełączenie układu.

Przykład układu elastycznego
Rysunek 1. Przykład układu elastycznego.

Wybierz dokładny układ

Jeśli nie możesz wykorzystać małego zestawu układów elastycznych, różne układy dostosowane do rozmiarów, w których wyświetla się widżet. To jest zwykle dwóch rozmiarów dla telefonów (tryb pionowy i poziomy) oraz 4 rozmiary dla urządzeń składanych.

Aby wdrożyć to rozwiązanie, aplikacja musi wykonać te czynności:

  1. Przeładuj AppWidgetProvider.onAppWidgetOptionsChanged(), która jest wywoływana, gdy zmienia się zbiór rozmiarów.

  2. Zadzwoń pod numer AppWidgetManager.getAppWidgetOptions(). , który zwraca Bundle zawierający rozmiary.

  3. Otwórz klucz AppWidgetManager.OPTION_APPWIDGET_SIZES w sekcji Bundle.

Ten przykładowy kod pokazuje, jak określić dokładne układy.

Kotlin

override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        id: Int,
        newOptions: Bundle?
) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions)
    // Get the new sizes.
    val sizes = newOptions?.getParcelableArrayList<SizeF>(
            AppWidgetManager.OPTION_APPWIDGET_SIZES
    )
    // Check that the list of sizes is provided by the launcher.
    if (sizes.isNullOrEmpty()) {
        return
    }
    // Map the sizes to the RemoteViews that you want.
    val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
    appWidgetManager.updateAppWidget(id, remoteViews)
}

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }

Java

@Override
public void onAppWidgetOptionsChanged(
    Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
        newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the RemoteViews that you want.
    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
        viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

// Create the RemoteViews for the given size.
private RemoteViews createRemoteViews(SizeF size) { }

Określanie rozmiaru widżetu

Każdy widżet musi definiować targetCellWidth i targetCellHeight dla urządzeń z Androidem 12 lub nowszym albo z systemem minWidth i minHeight w przypadku wszystkich wersji Androida (oznaczające minimalną ilość wykorzystywanego miejsca) domyślnie. Jednak gdy użytkownicy dodają widżet do ekranu głównego, zajmuje on więcej niż minimalną szerokość i wysokość określone przez Ciebie.

Ekrany główne Androida oferują użytkownikom siatkę dostępnych miejsc, w których mogą umieszczać widżety i ikony. Siatka może się różnić w zależności od urządzenia. Na przykład wiele telefonów ma siatkę 5 x 4, a tablety mogą mieć większą siatkę. Gdy widżet po dodaniu, rozciąga się, aby zająć minimalną liczbę komórek, w poziomie i w pionie, wymagane do spełnienia ograniczeń targetCellWidth i targetCellHeight na urządzeniach z uruchomionymi Android 12 lub nowszy albo ograniczenia minWidth i minHeight są włączone urządzeń z Androidem 11 (poziom interfejsu API 30) lub niższym.

szerokość i wysokość komórki oraz rozmiar zastosowanych automatycznych marginesów; mogą się różnić w zależności od urządzenia. Użyj tej tabeli, aby w przybliżeniu oszacować widżetu w typowym telefonie z siatką 5x4, biorąc pod uwagę liczba zajętych komórek siatki, które chcesz uwzględnić:

Liczba komórek (szerokość x wysokość) Dostępny rozmiar w panoramie (dp) Dostępny rozmiar w trybie poziomym (dp)
1 x 1 57 x 102 dp 127 x 51 dp
2 x 1 130x102dp 269x51dp
3 x 1 203x102dp 412 x 51 dp
10 × 15 cm 276 x 102 dp 554 x 51 dp
13 × 18 cm 349x102dp 697 x 51 dp
5 x 2 349 x 220 px 697 x 117 dp
13 × 15 cm 349 x 337 dp 697 x 184 dp
5 x 4 349x455dp 697x250dp
...
N × M (73 n – 16) x (118 m – 16) (142 n – 15) x (66 m–15)

Użyj rozmiarów komórek w trybie poziomym, aby podać wartości atrybutów minWidth, minResizeWidth i maxResizeWidth. Podobnie, używaj rozmiarów komórek w trybie poziomym, aby podać wartości atrybutów minHeight, minResizeHeight i maxResizeHeight.

Dzieje się tak, ponieważ szerokość komórki jest zazwyczaj mniejsza w trybie pionowym niż w trybie poziomym, a podobnie wysokość komórki jest zazwyczaj mniejsza w trybie poziomym niż w trybie pionowym.

Jeśli na przykład chcesz, aby widget można było zmniejszyć do rozmiaru jednej komórki na telefonie Google Pixel 4, musisz ustawić wartość minResizeWidth na co najwyżej 56 dp, aby mieć pewność, że wartość atrybutu minResizeWidth jest mniejsza niż 57 dp, ponieważ komórka ma co najmniej 57 dp szerokości w orientacji poziomej. Jeśli chcesz, by wysokość widżetu można było zmieniać w jednej komórce na tym samym urządzeniu, ustaw minResizeHeight na maksymalnie 50 dp, wartość atrybutu minResizeHeight jest mniejsza niż 51 dp – ponieważ jedna komórka ma wysokość co najmniej 51 dp w trybie poziomym.

Rozmiar każdego widżetu można zmieniać w zakresie rozmiarów między atrybutami minResizeWidth/minResizeHeightmaxResizeWidth/maxResizeHeight, co oznacza, że musi on dostosowywać się do wszystkich rozmiarów między nimi.

Aby na przykład ustawić domyślny rozmiar widżetu w miejscu docelowym, możesz ustaw te atrybuty:

<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

Oznacza to, że domyślny rozmiar widżetu to 3 x 2 komórki, zgodnie z Atrybuty targetCellWidth i targetCellHeight (lub 180 × 110 dp), określone przez minWidth i minHeight dla urządzeń z Android 11 lub starszy. W tym drugim przypadku rozmiar w komórkach różnią się w zależności od urządzenia.

Aby ustawić obsługiwane zakresy rozmiarów widżetu, możesz ustawić następujące opcje: atrybuty:

<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

Zgodnie z poprzednimi atrybutami szerokość widżetu można zmieniać w zakresie od 180 dp do 530 dp, a wysokość – od 110 dp do 450 dp. Rozmiar widżetu można zmienić z komórek 3 x 2 na 5 x 2, pod warunkiem że: występują warunki:

Kotlin

val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small)
val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium)
val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large)

val viewMapping: Map<SizeF, RemoteViews> = mapOf(
        SizeF(180f, 110f) to smallView,
        SizeF(270f, 110f) to mediumView,
        SizeF(270f, 280f) to largeView
)

appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))

Java

RemoteViews smallView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small);
RemoteViews mediumView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium);
RemoteViews largeView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large);

Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
viewMapping.put(new SizeF(180f, 110f), smallView);
viewMapping.put(new SizeF(270f, 110f), mediumView);
viewMapping.put(new SizeF(270f, 280f), largeView);
RemoteViews remoteViews = new RemoteViews(viewMapping);

appWidgetManager.updateAppWidget(id, remoteViews);

Załóżmy, że widżet korzysta z elastycznego układu zdefiniowanego w poprzednich fragmentach kodu. Oznacza to, że układ określony jako R.layout.widget_weather_forecast_small jest używany w zakresie 180 dp (minResizeWidth) x 110 dp (minResizeHeight) do 269 x 279 dp (kolejne punkty przecięcia – 1). Podobnie Zasób R.layout.widget_weather_forecast_medium jest używany od 270 x 110 dp do 270 x 279 dp, a R.layout.widget_weather_forecast_large jest używane od 270 x 280 dp do 530dp (maxResizeWidth) x 450dp (maxResizeHeight).

Gdy użytkownik zmienia rozmiar widżetu, jego wygląd dostosowuje się do rozmiaru komórki zgodnie z poniższymi przykładami.

Przykład widżetu pogody w najmniejszym rozmiarze 3 x 2. Interfejs wyświetla nazwę lokalizacji (Tokio), temperaturę (14°) i symbol wskazujący na częściowe zachmurzenie.
Rys. 2. 3 x 2 R.layout.widget_weather_forecast_small.

Przykład widżetu pogody w rozmiarze „średni” 4 x 2. Zmiana rozmiaru widżetu w ten sposób powoduje, że wszystkie elementy interfejsu z poprzedniego rozmiaru widżetu są zachowane, a dodatkowo wyświetlana jest etykieta „Pochmurnie” oraz prognoza temperatury od godziny 16:00 do 19:00.
Rysunek 3. 4 x 2 R.layout.widget_weather_forecast_medium.

Przykład widżetu pogody w rozmiarze „średni” 5 x 2. Zmiana rozmiaru widżetu w ten sposób powoduje, że interfejs wygląda tak samo jak w poprzednim rozmiarze, ale jest rozciągnięty o długość jednej komórki, aby zajmował więcej miejsca na poziomo.
Rysunek 4. 5 x 2 R.layout.widget_weather_forecast_medium

Przykładowy widżet pogody w dużym rozmiarze 5 x 3 rozmiaru. Zmiana rozmiaru widżetu
            w ten sposób bazuje na całym interfejsie użytkownika
z poprzednich rozmiarów widżetów,
            i dodaje do widżetu widok z prognozą pogody
            we wtorek i w środę. Symbole wskazujące na pogodę słoneczną lub deszczową oraz najwyższe i najniższe temperatury w każdym dniu.
Rysunek 5. 5 x 3 R.layout.widget_weather_forecast_large

Przykład widżetu pogody w dużej wersji 5 x 4. Zmiana rozmiaru widżetu w ten sposób opiera się na wszystkich elementach interfejsu z poprzednich rozmiarów widżetu i dodaje informacje dotyczące czwartku i piątku (oraz odpowiednie symbole wskazujące typ pogody oraz najwyższą i najniższą temperaturę w danym dniu).
Rysunek 6. 5x4 R.layout.widget_weather_forecast_large.