Umieszczenie aktywności pozwala zoptymalizować aplikacje na urządzeniach z dużym ekranem, dzieląc okno zadania aplikacji między 2 działania lub 2 instancje tej samej aktywności.
Jeśli aplikacja obejmuje wiele aktywności, dzięki osadzeniu aktywności możesz zapewnić użytkownikom lepsze wrażenia na tabletach, urządzeniach składanych i urządzeniach z ChromeOS.
Umieszczanie aktywności nie wymaga refaktoryzacji kodu. Aby określić, w jaki sposób aplikacja wyświetla swoje działania – równolegle lub warstwowo – możesz utworzyć plik konfiguracyjny XML lub wywołać interfejs API Jetpack WindowManager.
Obsługa małych ekranów zostanie utrzymana automatycznie. Gdy aplikacja działa na urządzeniu z małym ekranem, aktywności są układane jedna nad drugą. Na dużych ekranach aktywności są wyświetlane obok siebie. System określa prezentację na podstawie utworzonej przez Ciebie konfiguracji – nie jest wymagane rozgałęzienie.
Wbudowanie aktywności umożliwia zmianę orientacji urządzenia i płynnie działa na urządzeniach składanych oraz podczas składania i rozkładania urządzenia w stos.
Umieszczanie aktywności jest obsługiwane na większości dużych urządzeń z Androidem 12L (poziom interfejsu API 32) lub nowszym.
Podzielone okno zadania
Wstawianie aktywności powoduje podział okna zadań aplikacji na 2 kontenery: główny i dodatkowy. Kontenery zawierają działania uruchomione z głównej aktywności lub innych działań, które już znajdują się w kontenerach.
Działania są umieszczane w kontenerze dodatkowym w chwili ich uruchamiania, a dodatkowy kontener – nad kontenerami głównym na małych ekranach, więc ich grupowanie i nawigacja wstecz są zgodne z kolejnością aktywności wbudowanych w aplikację.
Umieszczanie aktywności umożliwia wyświetlanie aktywności na różne sposoby. Aplikacja może podzielić okno zadania, uruchamiając 2 działania obok siebie:
Działanie, które zajmuje całe okno zadania, może też spowodować podział przez uruchomienie nowej aktywności razem z tymi:
Działania, które są już podzielone i udostępniane okno zadania, mogą uruchamiać inne działania na te sposoby:
Obok innej aktywności:
Z boku i przesuń podział na bok, ukrywając poprzednią aktywność główną:
Uruchom działanie na górze, tzn. w tym samym stosie aktywności:
Uruchamianie całego okna działania w tym samym zadaniu:
Wstecz
Różne rodzaje aplikacji mogą mieć różne reguły nawigacji wstecz w stanie podzielonego okna zadań w zależności od zależności między działaniami lub sposobu wywoływania zdarzenia wstecz przez użytkowników:
- Połączenie: jeśli działania są powiązane i jedna z nich nie powinna być widoczna bez drugiej, można skonfigurować nawigację wsteczną tak, by kończyła obie.
- Jak to się robi? Jeśli działania są w pełni niezależne, wsteczna nawigacja po działaniu nie wpływa na stan innego działania w oknie zadania.
Podczas korzystania z nawigacji przy użyciu przycisków zdarzenie wstecz jest wysyłane do ostatniej zaznaczonej aktywności. W przypadku nawigacji opartej na gestach zdarzenie przejścia wstecz jest wysyłane do aktywności, w której wystąpił gest.
Układ z wieloma panelami
Jetpack WindowManager umożliwia tworzenie aktywności obejmującej układ z wieloma panelami na urządzeniach z dużymi ekranami z Androidem 12L (poziom interfejsu API 32) lub nowszym oraz na niektórych urządzeniach z wcześniejszymi wersjami platformy. Istniejące aplikacje, które opierają się na wielu działaniach, a nie na fragmentach czy układach opartych na widokach, np. SlidingPaneLayout
, mogą zapewnić lepsze wrażenia użytkownikom dużych ekranów bez refaktoryzacji kodu źródłowego.
Typowy przykład to podział na szczegóły listy. Aby zapewnić wysoką jakość prezentacji, system rozpoczyna działanie związane z listą, po czym aplikacja od razu rozpoczyna działanie związane ze szczegółowymi informacjami. System przejścia czeka, aż obie aktywności zostaną narysowane, a potem wyświetli je razem. Dla użytkownika obie aktywności są uruchamiane jako jedno.
Podziel atrybuty
Możesz określić proporcje okna zadania między podzielonymi kontenerami oraz położenie kontenerów względem siebie.
W przypadku reguł zdefiniowanych w pliku konfiguracji XML ustaw następujące atrybuty:
splitRatio
: ustawia proporcje kontenera. Wartość jest liczbą zmiennoprzecinkową w odstępie otwartym (0,0; 1,0).splitLayoutDirection
: określa sposób układania podzielonych kontenerów względem siebie. Dostępne wartości:ltr
: od lewej do prawejrtl
: od prawej do lewejlocale
: wartośćltr
lubrtl
jest określana na podstawie ustawień regionalnych
Przykłady znajdziesz poniżej w sekcji Konfiguracja XML.
W przypadku reguł utworzonych przy użyciu interfejsów WindowManager API utwórz obiekt SplitAttributes
z SplitAttributes.Builder
i wywołaj te metody kreatora:
setSplitType()
: określa proporcje podzielonych kontenerów. Prawidłowe argumenty, w tym metodęSplitAttributes.SplitType.ratio()
, znajdziesz w sekcjiSplitAttributes.SplitType
.setLayoutDirection()
: określa układ kontenerów. Możliwe wartości znajdziesz w sekcjiSplitAttributes.LayoutDirection
.
Przykłady znajdziesz poniżej w sekcji WindowManager API.
Symbole zastępcze
Działania zastępcze to puste aktywności dodatkowe, które zajmują dany obszar podziału aktywności. Mają one zastąpić inne działanie, które zawiera treści. Na przykład aktywność zastępcza może zajmować drugorzędną stronę podziału aktywności w układzie ze szczegółami listy, dopóki nie zostanie wybrany element z listy. Wtedy aktywność zawierającą szczegółowe informacje dotyczące wybranego elementu listy zastąpi obiekt zastępczy.
Domyślnie system wyświetla symbole zastępcze tylko wtedy, gdy jest wystarczająco dużo miejsca na podział aktywności. Symbole zastępcze automatycznie kończą się, gdy rozmiar wyświetlacza zmieni się na szerokość lub wysokość, która jest za mała, aby można było wyświetlić podział. Gdy pozwala na to miejsce, system uruchamia symbol zastępczy z powrotem zainicjowanym.
Jednak atrybut stickyPlaceholder
metody SplitPlaceholderRule
lub setSticky()
w SplitPlaceholder.Builder
może zastąpić działanie domyślne. Gdy atrybut lub metoda określa wartość true
, system wyświetla obiekt zastępczy jako najwyższą aktywność w oknie zadania, gdy rozmiar wyświetlacza zostanie zmniejszony do rozmiaru pojedynczego panelu (przykład znajdziesz w sekcji Konfiguracja podziału).
Zmiana rozmiaru okna
Gdy zmiany w konfiguracji urządzenia zmniejszają szerokość okna zadania, tak aby nie mieściło się ono w układzie z wieloma panelami (np. gdy duży ekran składa się z tabletu do rozmiaru telefonu lub okno aplikacji zmienia się w trybie wielu okien), aktywności inne niż zastępcze w panelu dodatkowym okna zadania są ułożone nad aktywnościami w panelu głównym.
Działania zastępcze są wyświetlane tylko wtedy, gdy szerokość wyświetlania jest wystarczająca do podziału. Na mniejszych ekranach obiekt zastępczy jest automatycznie zamykany. Gdy obszar wyświetlania ponownie stanie się wystarczająco duży, obiekt zastępczy zostanie odtworzony. (zobacz Symbole zastępcze powyżej).
Stosowanie działań jest możliwe, ponieważ funkcja WindowManager porządkuje aktywności w panelu dodatkowym nad działaniami w panelu głównym.
Wiele działań w panelu dodatkowym
Aktywność B rozpoczyna działanie C bez dodatkowych flag intencji:
daje to działanie w tej kolejności zależnej od kolejności działań w ramach tego samego zadania:
W mniejszym oknie zadania aplikacja zmniejsza się do 1 działania z C na górze stosu:
Powrót do mniejszego okna powoduje przechodzenie między działaniami ułożonymi na siebie.
Jeśli konfiguracja okna zadania zostanie przywrócona do większego rozmiaru, który może pomieścić wiele paneli, działania będą ponownie wyświetlane obok siebie.
Skumulowane podziały
Działanie B rozpoczyna działanie C na bok i przesuwa podział na boki:
W efekcie działania w ramach tego samego zadania mają taką kolejność (z-order):
W mniejszym oknie zadania aplikacja zmniejsza się do 1 działania, a u góry znajduje się klawisz C:
Stała orientacja pionowa
Ustawienie w pliku manifestu android:screenOrientation umożliwia aplikacjom ograniczenie aktywności do orientacji pionowej lub poziomej. Aby zwiększyć wygodę użytkowników korzystających z urządzeń z dużymi ekranami, takich jak tablety i urządzenia składane, producenci urządzeń (OEM) mogą ignorować prośby o orientację ekranu oraz wyświetlać aplikację w orientacji pionowej na wyświetlaczu poziomym lub poziomą na wyświetlaczu pionowym.
Podobnie gdy włączone jest umieszczanie aktywności, producenci OEM mogą dostosowywać urządzenia do aktywności w orientacji poziomej w orientacji poziomej na dużych ekranach (szerokość ≥ 600 dp). Gdy aktywność w ustalonym orientacji pionowej uruchamia drugą aktywność, urządzenie może wyświetlić je obok siebie na wyświetlaczu na 2 panelach.
Zawsze dodawaj właściwość android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
do pliku manifestu aplikacji, aby informować urządzenia, że aplikacja obsługuje umieszczanie aktywności (patrz Konfiguracja podziału poniżej). Urządzenia dostosowane do OEM mogą następnie określić, czy mają być wyświetlane aktywności w orientacji pionowej.
Konfiguracja podziału
Reguły podziału określają podziały aktywności. Reguły podziału definiuje się w pliku konfiguracji XML lub przez wywołania interfejsu API usługi Jetpack WindowManager.
W obu przypadkach aplikacja musi mieć dostęp do biblioteki WindowManager i musi informować system o umieszczaniu aktywności w aplikacji.
Wykonaj te czynności:
Dodaj najnowszą zależność biblioteki WindowManagera do pliku
build.gradle
na poziomie modułu aplikacji, na przykład:implementation 'androidx.window:window:1.1.0-beta02'
Biblioteka WindowManager zawiera wszystkie komponenty wymagane do umieszczania aktywności.
Poinformuj system, że w Twojej aplikacji jest zaimplementowana umieszczanie aktywności.
Dodaj właściwość
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
do elementu <application> pliku manifestu aplikacji i ustaw wartość na true, np.:<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <property android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" android:value="true" /> </application> </manifest>
W systemie WindowManager w wersji 1.1.0-alfa06 i nowszych podziały umieszczania aktywności są wyłączone, chyba że zostanie dodana właściwość do pliku manifestu i ma wartość Prawda.
Producenci urządzeń włączają też niestandardowe funkcje w aplikacjach, które obsługują umieszczanie aktywności. Urządzenia mogą na przykład wyświetlać aktywność pionową tylko w orientacji pionowej na wyświetlaczu poziomym, aby ułatwić przejście na układ z 2 panelami po rozpoczęciu drugiej aktywności (patrz Stała orientacja pionowa).
Konfiguracja XML
Aby utworzyć implementację osadzoną aktywności opartą na języku XML, wykonaj te czynności:
Utwórz plik XML zasobów, który:
- Definiuje aktywności wspólne dla
- Konfigurowanie opcji podziału
- Tworzy obiekt zastępczy dla dodatkowego kontenera podziału, gdy treść jest niedostępna.
- Określa działania, które nigdy nie powinny być częścią podziału
Na przykład:
<!-- main_split_config.xml --> <resources xmlns:window="http://schemas.android.com/apk/res-auto"> <!-- Define a split for the named activities. --> <SplitPairRule window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:finishPrimaryWithSecondary="never" window:finishSecondaryWithPrimary="always" window:clearTop="false"> <SplitPairFilter window:primaryActivityName=".ListActivity" window:secondaryActivityName=".DetailActivity"/> </SplitPairRule> <!-- Specify a placeholder for the secondary container when content is not available. --> <SplitPlaceholderRule window:placeholderActivityName=".PlaceholderActivity" window:splitRatio="0.33" window:splitLayoutDirection="locale" window:splitMinWidthDp="840" window:splitMaxAspectRatioInPortrait="alwaysAllow" window:stickyPlaceholder="false"> <ActivityFilter window:activityName=".ListActivity"/> </SplitPlaceholderRule> <!-- Define activities that should never be part of a split. Note: Takes precedence over other split rules for the activity named in the rule. --> <ActivityRule window:alwaysExpand="true"> <ActivityFilter window:activityName=".ExpandedActivity"/> </ActivityRule> </resources>
Utwórz inicjator.
Komponent WindowManager
RuleController
analizuje plik konfiguracji XML i udostępnia reguły systemowi. Biblioteka startup JetpackInitializer
udostępnia plik XMLRuleController
podczas uruchamiania aplikacji. Dzięki temu reguły zaczynają obowiązywać po rozpoczęciu jakichkolwiek działań.Aby utworzyć inicjator, wykonaj te czynności:
Dodaj najnowszą zależność z biblioteką startową Jetpack do pliku
build.gradle
na poziomie modułu, na przykład:implementation 'androidx.startup:startup-runtime:1.1.1'
Utwórz klasę, która implementuje interfejs
Initializer
.Inicjator udostępnia reguły podziału w metodzie
RuleController
, przekazując do metodyRuleController.parseRules()
identyfikator pliku konfiguracji XML (main_split_config.xml
).Kotlin
class SplitInitializer : Initializer<RuleController> { override fun create(context: Context): RuleController { return RuleController.getInstance(context).apply { setRules(RuleController.parseRules(context, R.xml.main_split_config)) } } override fun dependencies(): List<Class<out Initializer<*>>> { return emptyList() } }
Java
public class SplitInitializer implements Initializer<RuleController> { @NonNull @Override public RuleController create(@NonNull Context context) { RuleController ruleController = RuleController.getInstance(context); ruleController.setRules( RuleController.parseRules(context, R.xml.main_split_config) ); return ruleController; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }
Utwórz dostawcę treści na potrzeby definicji reguł.
Dodaj
androidx.startup.InitializationProvider
do pliku manifestu aplikacji jako<provider>
. Dołącz odniesienie do implementacji inicjatoraRuleController
,SplitInitializer
:<!-- AndroidManifest.xml --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- Make SplitInitializer discoverable by InitializationProvider. --> <meta-data android:name="${applicationId}.SplitInitializer" android:value="androidx.startup" /> </provider>
InitializationProvider
wykrywa i inicjujeSplitInitializer
przed wywołaniem metodyonCreate()
w aplikacji. W efekcie reguły podziału obowiązują po rozpoczęciu głównej aktywności w aplikacji.
Interfejs API WindowManager
Możesz zaimplementować umieszczanie aktywności automatycznie za pomocą kilku wywołań interfejsu API. Wywołaj metodę onCreate()
podklasy Application
, aby mieć pewność, że reguły działają przed uruchomieniem jakichkolwiek działań.
Aby automatycznie utworzyć podział działań:
Utwórz regułę podziału:
Utwórz element
SplitPairFilter
, który wskaże aktywności wspólne dla tego podziału:Kotlin
val splitPairFilter = SplitPairFilter( ComponentName(this, ListActivity::class.java), ComponentName(this, DetailActivity::class.java), null )
Java
SplitPairFilter splitPairFilter = new SplitPairFilter( new ComponentName(this, ListActivity.class), new ComponentName(this, DetailActivity.class), null );
Aby dodać filtr do zestawu filtrów:
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
Utwórz atrybuty układu dla podziału:
Kotlin
val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build()
Java
final SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build();
SplitAttributes.Builder
tworzy obiekt zawierający atrybuty układu:setSplitType
: określa sposób przypisywania dostępnego obszaru wyświetlania do każdego kontenera aktywności. Typ podziału współczynnika określa proporcje dostępnego obszaru wyświetlania przydzielonego do kontenera głównego. Kontener dodatkowy zajmuje pozostałą część dostępnego obszaru wyświetlania.setLayoutDirection
: określa układ kontenerów aktywności względem siebie (najpierw kontener główny).
Utwórz
SplitPairRule
:Kotlin
val splitPairRule = SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build()
Java
SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build();
SplitPairRule.Builder
tworzy i konfiguruje regułę:filterSet
: zawiera filtry podzielonej par, które określają, kiedy należy zastosować regułę, identyfikując aktywności mające ten sam podział.setDefaultSplitAttributes
: stosuje do reguły atrybuty układu.setMinWidthDp
: określa minimalną szerokość wyświetlacza (w pikselach niezależnych od gęstości, dp), która umożliwia podział.setMinSmallestWidthDp
: określa minimalną wartość (w dp), którą musi mieć mniejszy z dwóch wymiarów wyświetlacza, aby umożliwić podział niezależnie od orientacji urządzenia.setMaxAspectRatioInPortrait
: określa maksymalny współczynnik proporcji wyświetlacza (wysokość:szerokość) w orientacji pionowej, dla której wyświetlane są podziały aktywności. Jeśli współczynnik proporcji ekranu pionowego przekracza maksymalny współczynnik proporcji, podziały są wyłączone niezależnie od szerokości wyświetlacza. Uwaga: wartość domyślna to 1,4, co oznacza, że na większości tabletów działania zajmują całe okno zadania w orientacji pionowej. Zobacz teżSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
isetMaxAspectRatioInLandscape
. Domyślna wartość dla orientacji poziomej toALWAYS_ALLOW
.setFinishPrimaryWithSecondary
: określa, jak zakończenie wszystkich działań w kontenerze dodatkowym wpływa na aktywności w kontenerze głównym.NEVER
oznacza, że system nie powinien kończyć działań głównych, gdy wszystkie działania w kontenerze dodatkowym zostaną zakończone (patrz Kończenie działań).setFinishSecondaryWithPrimary
: określa, jak zakończenie wszystkich działań w kontenerze głównym wpływa na aktywności w kontenerze dodatkowym.ALWAYS
oznacza, że system powinien zawsze zakończyć działania w kontenerze dodatkowym po zakończeniu wszystkich działań w kontenerze podstawowym (patrz Zakończenie działań).setClearTop
: określa, czy wszystkie działania w kontenerze dodatkowym mają kończyć się wraz z uruchomieniem w nim nowej aktywności. Wartość false oznacza, że nowe działania są nakładane na działania, które już znajdują się w kontenerze dodatkowym.
Pobierz pojedyncze wystąpienie WindowManagera
RuleController
i dodaj regułę:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
Utwórz zastępczą kontener dodatkowego, gdy treść jest niedostępna:
Utwórz element
ActivityFilter
identyfikujący aktywność, z którą obiekt zastępczy ma wspólne okno zadania:Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
Aby dodać filtr do zestawu filtrów:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
Utwórz
SplitPlaceholderRule
:Kotlin
val splitPlaceholderRule = SplitPlaceholderRule.Builder( placeholderActivityFilterSet, Intent(context, PlaceholderActivity::class.java) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build()
Java
SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder( placeholderActivityFilterSet, new Intent(context, PlaceholderActivity.class) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build();
SplitPlaceholderRule.Builder
tworzy i konfiguruje regułę:placeholderActivityFilterSet
: zawiera filtry aktywności, które określają, kiedy zastosować regułę, identyfikując działania, z którymi powiązana jest aktywność zastępcza.Intent
: określa uruchomienie aktywności zastępczej.setDefaultSplitAttributes
: stosuje do reguły atrybuty układu.setMinWidthDp
: określa minimalną szerokość wyświetlacza (w pikselach niezależnych od gęstości, dp), która umożliwia podział.setMinSmallestWidthDp
: określa minimalną wartość (w dp), którą musi mieć mniejszy z dwóch wymiarów wyświetlacza, aby można było podzielić dane niezależnie od orientacji urządzenia.setMaxAspectRatioInPortrait
: określa maksymalny współczynnik proporcji wyświetlacza (wysokość:szerokość) w orientacji pionowej, dla której wyświetlane są podziały aktywności. Uwaga: wartość domyślna to 1,4, co na większości tabletów oznacza, że czynności wypełniają okno zadania w orientacji pionowej. Zobacz teżSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
isetMaxAspectRatioInLandscape
. Domyślna wartość dla orientacji poziomej toALWAYS_ALLOW
.setFinishPrimaryWithPlaceholder
: określa, jak zakończenie działania obiektu zastępczego wpływa na działania w kontenerze głównym. Opcja ZAWSZE oznacza, że system powinien zawsze zakończyć działania w kontenerze głównym po zakończeniu działania symbolu zastępczego (patrz Zakończenie działań).setSticky
: określa, czy aktywność zastępcza pojawia się na górze stosu aktywności na małych ekranach, gdy po raz pierwszy pojawi się ona w części z odpowiednią minimalną szerokością.
Dodaj regułę do WindowManagera
RuleController
:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
Określ działania, które nigdy nie powinny być częścią podziału:
Utwórz element
ActivityFilter
identyfikujący aktywność, która zawsze powinna zajmować cały obszar wyświetlania zadania:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
Aby dodać filtr do zestawu filtrów:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
Utwórz
ActivityRule
:Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ActivityRule.Builder
tworzy i konfiguruje regułę:expandedActivityFilterSet
: zawiera filtry aktywności określające, kiedy zastosować regułę, identyfikując aktywności, które chcesz wykluczyć z podziałów.setAlwaysExpand
: określa, czy aktywność ma wypełnić całe okno zadania.
Dodaj regułę do WindowManagera
RuleController
:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
Umieszczanie w różnych aplikacjach
Na Androidzie 13 (poziom interfejsu API 33) i nowszych aplikacje mogą umieszczać aktywności z innych aplikacji. Umieszczanie aktywności między aplikacjami lub na podstawie identyfikatorów UID – umożliwia wizualną integrację działań z wielu aplikacji na Androida. System wyświetla aktywność aplikacji hosta i umieszczoną aktywność z innej aplikacji na ekranie obok siebie lub u góry, tak jak w przypadku umieszczania aktywności w pojedynczej aplikacji.
Na przykład w aplikacji Ustawienia można umieścić aktywność selektora tapety z aplikacji TapPicker:
Model zaufania
Procesy hostujące, które zawierają działania z innych aplikacji, mogą zmienić sposób prezentacji tych działań, w tym rozmiar, położenie, przycięcie i przezroczystość. Szkodliwe hosty mogą wykorzystywać tę możliwość, by wprowadzić użytkowników w błąd i stworzyć tzw. przechwytywanie kliknięć lub inne ataki polegające na odzyskiwaniu dostępu do interfejsu.
Aby zapobiec niewłaściwemu użyciu umieszczania aktywności w różnych aplikacjach, Android wymaga, aby aplikacje zezwalały na umieszczanie na stronach tych aktywności. Aplikacje mogą oznaczać hosty jako zaufane lub niezaufane.
Zaufane hosty
Aby umożliwić innym aplikacjom umieszczanie i pełną kontrolę nad ich prezentowaniem, podaj certyfikat SHA-256 aplikacji hosta w atrybucie android:knownActivityEmbeddingCerts
elementu <activity>
lub <application>
pliku manifestu aplikacji.
Ustaw wartość android:knownActivityEmbeddingCerts
w postaci ciągu znaków:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
lub, aby określić wiele certyfikatów, użyj tablicy ciągów znaków:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
który odwołuje się do zasobu takiego jak:
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
Właściciele aplikacji mogą uzyskać podsumowanie certyfikatu SHA, uruchamiając zadanie signingReport
Gradle. Skrót certyfikatu to odcisk cyfrowy SHA-256 bez dwukropków oddzielających. Więcej informacji znajdziesz w artykułach Generowanie raportu podpisywania i Uwierzytelnianie klienta.
Niezaufane hosty
Aby umożliwić dowolnej aplikacji umieszczanie aktywności z aplikacji i kontrolowanie ich prezentacji, określ atrybut android:allowUntrustedActivityEmbedding
w elementach <activity>
lub <application>
w manifeście aplikacji, na przykład:
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
Domyślna wartość tego atrybutu to false (fałsz), co uniemożliwia umieszczanie aktywności w różnych aplikacjach.
Uwierzytelnianie niestandardowe
Aby ograniczyć ryzyko związane z umieszczaniem niezaufanej aktywności, utwórz niestandardowy mechanizm uwierzytelniania, który weryfikuje tożsamość hosta. Jeśli znasz certyfikaty hosta, użyj biblioteki androidx.security.app.authenticator
do uwierzytelniania. Jeśli po umieszczeniu aktywności host uwierzytelni się po umieszczeniu przez Ciebie aktywności, możesz wyświetlić rzeczywistą treść. Jeśli nie, możesz poinformować użytkownika, że dane działanie jest niedozwolone, i zablokować treści.
Użyj metody ActivityEmbeddingController#isActivityEmbedded()
z biblioteki Jetpack WindowManager, aby sprawdzić, czy host umieszcza Twoją aktywność, na przykład:
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
Ograniczenie minimalnego rozmiaru
System Android stosuje do osadzonych aktywności minimalną wysokość i szerokość określone w elemencie manifestu aplikacji <layout>
. Jeśli aplikacja nie określa minimalnej wysokości i szerokości, stosowane są wartości domyślne systemu (sw220dp
).
Jeśli host spróbuje zmienić rozmiar umieszczonego kontenera tak, aby jego rozmiar był mniejszy niż minimalny, zostanie on rozszerzony i zajmie całe granice zadania.
<alias-aktywności>
Aby osadzanie zaufanych lub niezaufanych aktywności z elementem <activity-alias>
działało, do docelowej aktywności należy zastosować parametr android:knownActivityEmbeddingCerts
lub android:allowUntrustedActivityEmbedding
, a nie alias. Zasada sprawdzająca bezpieczeństwo serwera systemowego opiera się na flagach ustawionych na serwerze docelowym, a nie na aliasie.
Aplikacja hosta
Aplikacje hostujące implementują umieszczanie aktywności w różnych aplikacjach w taki sam sposób, w jaki implementują umieszczanie aktywności w pojedynczej aplikacji. Obiekty SplitPairRule
, SplitPairFilter
lub ActivityRule
oraz ActivityFilter
określają osadzone aktywności i podziały okien zadań. Reguły podziału są definiowane statycznie w pliku XML lub w czasie działania za pomocą wywołań interfejsu API Jetpack WindowManager.
Jeśli aplikacja hostująca próbuje umieścić aktywność, która nie ma włączonej opcji umieszczania między aplikacjami, aktywność obejmuje wszystkie granice zadania. W związku z tym aplikacje hostujące muszą wiedzieć, czy aktywności docelowe umożliwiają umieszczanie w różnych aplikacjach.
Jeśli umieszczona aktywność będzie rozpoczynać nowe działanie w ramach tego samego zadania, a nowa aktywność nie ma włączonej opcji umieszczania między aplikacjami, aktywność obejmuje wszystkie granice zadania i nie nakłada się na nią w umieszczonym kontenerze.
Aplikacja hostująca może bez ograniczeń osadzać własne działania, o ile są one uruchamiane w tym samym zadaniu.
Przykłady podziału
Podziel od całego okna
Refaktoryzacja nie jest wymagana. Konfigurację podziału możesz określić statycznie lub w czasie działania, a potem wywołać metodę Context#startActivity()
bez dodatkowych parametrów.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Podziel domyślnie
Jeśli strona docelowa aplikacji jest zaprojektowana w taki sposób, aby podzielić ją na 2 kontenery na dużych ekranach, wygoda użytkowników działa najlepiej wtedy, gdy obie aktywności są tworzone i prezentowane jednocześnie. Jednak treść może być niedostępna w przypadku dodatkowego kontenera podziału, dopóki użytkownik nie wejdzie w interakcję z działaniem w kontenerze głównym (np. wybierze element z menu nawigacyjnego). Aktywność zastępcza może wypełnić lukę do czasu, gdy treść pojawi się w dodatkowym kontenerze podziału (patrz Symbole zastępcze powyżej).
Aby utworzyć podział za pomocą obiektu zastępczego, utwórz obiekt zastępczy i powiąż go z aktywnością główną:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
Podział precyzyjnych linków
Gdy aplikacja otrzyma intencję, aktywność docelowa może zostać wyświetlona jako dodatkowa część podziału aktywności, np. żądanie wyświetlenia ekranu z informacjami o elemencie z listy. Na małych ekranach szczegóły są widoczne w pełnym oknie zadania, a na większych urządzeniach – obok listy.
Żądanie uruchomienia powinno być kierowane do głównej aktywności, a działanie związane ze szczegółami celu powinno zostać uruchomione w częściach. System automatycznie wybierze odpowiednią prezentację (stojącą lub obok siebie) na podstawie dostępnej szerokości wyświetlacza.
Kotlin
override fun onCreate(savedInstanceState Bundle?) { . . . RuleController.getInstance(this) .addRule(SplitPairRule.Builder(filterSet).build()) startActivity(Intent(this, DetailActivity::class.java)) }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . RuleController.getInstance(this) .addRule(new SplitPairRule.Builder(filterSet).build()); startActivity(new Intent(this, DetailActivity.class)); }
Miejsce docelowe precyzyjnego linku może być jedyną aktywnością, która powinna być dostępna dla użytkownika w stosie nawigacji wstecznej. Lepiej nie zamykać działania szczegółów i pozostawiać tylko aktywności głównej:
Zamiast tego możesz wykonać obie aktywności jednocześnie, używając atrybutu finishPrimaryWithSecondary
:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
Zobacz Atrybuty konfiguracji poniżej.
Wiele aktywności w podzielonych kontenerach
Umieszczenie wielu działań w podzielonym kontenerze umożliwia użytkownikom dostęp do szczegółowych treści. Na przykład w przypadku podziału szczegółów listy użytkownik musi przejść do sekcji szczegółów podrzędnych, ale zachować główną aktywność:
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
Czynność związana ze szczegółami podrzędnymi jest umieszczona nad aktywnością związaną ze szczegółami, aby ją ukryć:
Użytkownik może wtedy wrócić na poprzedni poziom szczegółów, wracając do stosu:
Układanie aktywności na stosie to działanie domyślne, gdy aktywność jest uruchamiana z poziomu działania w tym samym kontenerze dodatkowym. Działania uruchomione z kontenera podstawowego w ramach aktywnego podziału trafiają też do dodatkowego kontenera na górze stosu aktywności.
Działania w nowym zadaniu
Gdy działania w podzielonym oknie zadań rozpoczynają działania w nowym zadaniu, nowe zadanie jest niezależne od zadania, które obejmuje podział, i jest wyświetlane w pełnym oknie. Na ekranie Ostatnie są widoczne 2 zadania: w podziale i w nowym.
Zastępowanie aktywności
Aktywności można zastępować w dodatkowym stosie kontenerów, np. gdy aktywność główna jest używana do nawigacji najwyższego poziomu, a aktywność dodatkowa jest wybranym miejscem docelowym. Każdy wybór z nawigacji najwyższego poziomu powinien rozpoczynać nowe działanie w kontenerze dodatkowym i usuwać działania, które wcześniej się tam znajdowały.
Jeśli po zmianie wyboru nawigacji aplikacja nie ukończy działania w kontenerze dodatkowym, nawigacja wsteczna może być dezorientująca, gdy podział jest zwinięty (gdy urządzenie jest złożone). Jeśli na przykład masz menu w panelu głównym, a ekrany A i B w panelu dodatkowym, po złożeniu telefonu przez użytkownika, element B znajduje się na panelu A, a A na górze menu. Gdy użytkownik cofnie się z miejsca B, zamiast menu pojawi się A.
W takich przypadkach ekran A musi zostać usunięty ze stosu.
Domyślnym działaniem w przypadku uruchamiania z boku w nowym kontenerze w przypadku obecnego podziału jest umieszczenie nowych kontenerów dodatkowych na wierzchu i zachowanie starych kontenerów w stosie wstecznym. Możesz skonfigurować podziały, aby wyczyścić poprzednie kontenery dodatkowe przy użyciu clearTop
i normalnie uruchamiać nowe działania.
<SplitPairRule
window:clearTop="true">
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenA"/>
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>
Kotlin
class MenuActivity { . . . fun onMenuItemSelected(selectedMenuItem: Int) { startActivity(Intent(this, classForItem(selectedMenuItem))) } }
Java
public class MenuActivity { . . . void onMenuItemSelected(int selectedMenuItem) { startActivity(new Intent(this, classForItem(selectedMenuItem))); } }
Możesz też użyć tej samej dodatkowej aktywności, a z działania głównego (menu) wyślij nowe intencje, które prowadzą do tej samej instancji, ale aktywują stan lub aktualizację interfejsu w kontenerze dodatkowym.
Wiele podziałów
Aplikacje mogą umożliwiać szczegółową nawigację wielopoziomową, udostępniając dodatkowe aktywności z boku.
Gdy działanie w kontenerze dodatkowym wywoła nową aktywność z boku, w miejscu obecnego podziału zostanie utworzony nowy podział.
Stos wsteczny zawiera wszystkie aktywności, które były wcześniej otwarte, więc użytkownicy mogą przejść do podziału A/B po ukończeniu C.
Aby utworzyć nowy podział, uruchom nową aktywność obok istniejącego kontenera dodatkowego. Zadeklaruj konfiguracje podziałów A/B i B/C oraz uruchom działanie C, normalnie od B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B { . . . fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
Java
public class B { . . . void onOpenC() { startActivity(new Intent(this, C.class)); } }
Reagowanie na zmiany stanu podziału
Różne aktywności w aplikacji mogą mieć elementy interfejsu, które pełnią tę samą funkcję. Przykładem może być element sterujący, który otwiera okno z ustawieniami konta.
Jeśli 2 działania, których elementy interfejsu są wspólne, występują w podziale, wyświetlanie tego elementu w obu z nich może być zbędne i może być mylące.
Aby dowiedzieć się, kiedy działania są podzielone, sprawdź proces SplitController.splitInfoList
lub zarejestruj detektor za pomocą SplitControllerCallbackAdapter
pod kątem zmian stanu podziału. Następnie odpowiednio dostosuj interfejs:
Kotlin
val layout = layoutInflater.inflate(R.layout.activity_main, null) val view = layout.findViewById<View>(R.id.infoButton) lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance. .collect { list -> view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE } } }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { . . . new SplitControllerCallbackAdapter(SplitController.getInstance(this)) .addSplitListener( this, Runnable::run, splitInfoList -> { View layout = getLayoutInflater().inflate(R.layout.activity_main, null); layout.findViewById(R.id.infoButton).setVisibility( splitInfoList.isEmpty() ? View.VISIBLE : View.GONE); }); }
Korutyny można uruchamiać w dowolnym stanie cyklu życia, ale zwykle są uruchamiane w stanie STARTED
, aby oszczędzać zasoby (więcej informacji znajdziesz w artykule Używanie współprogramów Kotlin ze komponentami rozpoznającymi cykl życia).
Wywołania zwrotne mogą być wykonywane w dowolnym stanie cyklu życia, w tym w przypadku zatrzymania aktywności. Słuchacze zwykle powinni być zarejestrowani w tym kraju (onStart()
) i niezarejestrowani w tym kraju: onStop()
.
Okno modalne
Niektóre działania uniemożliwiają użytkownikom interakcję z aplikacją do momentu wykonania określonego działania. Na przykład aktywność na ekranie logowania, ekran potwierdzenia zasad lub komunikat o błędzie. Działania modalne powinny nie pojawiać się w podziale.
Aktywność można wymusić, aby zawsze wypełniało okno zadania, korzystając z konfiguracji rozwijania:
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Zakończ ćwiczenia
Użytkownicy mogą kończyć czynności po obu stronach podziału, przesuwając palcem od krawędzi wyświetlacza:
Jeśli urządzenie jest skonfigurowane do używania przycisku Wstecz zamiast nawigacji przy użyciu gestów, dane wejściowe są wysyłane do aktywności, której użytkownik dotknął lub uruchomił jako ostatni.
Wpływ zakończenia wszystkich działań w kontenerze na kontener przeciwny zależy od konfiguracji podziału.
Atrybuty konfiguracji
Możesz określić atrybuty reguły podziału, aby skonfigurować, jak zakończenie wszystkich działań po jednej stronie podziału wpływa na aktywności po drugiej stronie podziału. Są to:
window:finishPrimaryWithSecondary
– jak zakończenie wszystkich działań w kontenerze dodatkowym wpływa na działania w kontenerze głównymwindow:finishSecondaryWithPrimary
– jak zakończenie wszystkich działań w kontenerze głównym wpływa na działania w kontenerze dodatkowym
Możliwe wartości atrybutów:
always
– zawsze kończ działania w powiązanym kontenerzenever
– nigdy nie kończ działań w powiązanym kontenerzeadjacent
– zakończ działania w powiązanym kontenerze, gdy oba kontenery wyświetlają się obok siebie, ale nie wtedy, gdy są one stosowe.
Na przykład:
<SplitPairRule
<!-- Do not finish primary container activities when all secondary container activities finish. -->
window:finishPrimaryWithSecondary="never"
<!-- Finish secondary container activities when all primary container activities finish. -->
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Konfiguracja domyślna
Gdy zakończy się wszystkie działania w jednym kontenerze, pozostały kontener zajmie całe okno:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Zakończcie razem zadania
Automatycznie dokończ działania w kontenerze głównym po zakończeniu wszystkich działań w kontenerze dodatkowym:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Automatycznie dokończ działania w kontenerze dodatkowym po zakończeniu wszystkich działań w kontenerze głównym:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Zakończ działania razem po zakończeniu wszystkich działań w kontenerze głównym lub dodatkowym:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Kończenie wielu działań w kontenerach
Jeśli w podzielonym kontenerze znajduje się wiele działań, zakończenie działania u dołu stosu nie spowoduje automatycznego zakończenia działań na górze.
Jeśli np. w kontenerze dodatkowym znajdują się 2 działania, kod C na początku B:
a konfigurację podziału określa konfiguracja działań A i B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
zakończenie ostatniej aktywności powoduje zachowanie podziału.
Zakończenie działania dolnego (głównego) kontenera dodatkowego nie powoduje usunięcia z niego działań znajdujących się na jego wierzchołku, więc zachowuje podział.
Wszystkie dodatkowe reguły dotyczące wspólnego kończenia działań, takie jak kończenie działania dodatkowego działaniem głównym, również są wykonywane:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Jeśli podział zostanie skonfigurowany tak, aby oba te elementy zostały ukończone:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Zmieniaj właściwości podziału w czasie działania
Nie można zmienić właściwości obecnie aktywnego i widocznego podziału. Zmiana reguł podziału wpływa na dodatkowe uruchamianie aktywności i nowe kontenery, ale nie na istniejące i aktywne podziały.
Aby zmienić właściwości aktywnych podziałów, dokończ aktywność dodatkową lub działania w ramach podziału i uruchom ponownie z nową konfiguracją.
Wyodrębnianie aktywności z podziału do pełnego okna
Utwórz nową konfigurację, która wyświetla pełne okno aktywności z boku, a następnie ponownie uruchom działanie z intencją, która przejdzie do tej samej instancji.
Sprawdzanie podzielonej obsługi w czasie działania
Umieszczanie aktywności jest obsługiwane na Androidzie 12L (poziom interfejsu API 32) i nowszych, ale jest też dostępne na niektórych urządzeniach z wcześniejszymi wersjami platformy. Aby sprawdzić dostępność funkcji w czasie działania, użyj właściwości SplitController.splitSupportStatus
lub metody SplitController.getSplitSupportStatus()
:
Kotlin
if (SplitController.getInstance(this).splitSupportStatus == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
Java
if (SplitController.getInstance(this).getSplitSupportStatus() == SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { // Device supports split activity features. }
Jeśli podziały nie są obsługiwane, aktywności są uruchamiane nad stosem aktywności (zgodnie z modelem umieszczania braku aktywności).
Zapobiegaj zastąpieniu systemu
Producenci urządzeń z Androidem (producenci oryginalnego sprzętu lub OEM) mogą wdrożyć umieszczanie aktywności jako funkcję systemu urządzenia. System określa podział reguł w przypadku aplikacji wielozadaniowych, zastępując działanie okien przez te aplikacje. Zastąpienie systemu wymusza przechodzenie aplikacji wielozadaniowych do trybu umieszczania aktywności zdefiniowanego przez system.
Osadzanie aktywności systemu może poprawić prezentację aplikacji za pomocą układów z wieloma panelami, np. list-detail, bez wprowadzania zmian w aplikacji. Umieszczanie aktywności w systemie może też jednak powodować nieprawidłowy układ aplikacji, błędy lub konflikty z umieszczaniem aktywności wdrożonym przez aplikację.
Aplikacja może zapobiegać umieszczaniu aktywności systemu w witrynach lub zezwalać na ich umieszczanie, ustawiając w pliku manifestu usługę, na przykład:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<property
android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
android:value="true|false" />
</application>
</manifest>
Nazwa właściwości jest zdefiniowana w obiekcie WindowWłaściwości Jetpack WindowManager.
Ustaw wartość false
, jeśli Twoja aplikacja obsługuje umieszczanie aktywności lub jeśli chcesz w inny sposób uniemożliwić systemowi stosowanie w aplikacji swoich reguł umieszczania aktywności. Ustaw wartość na true
, aby umożliwić systemowi stosowanie w aplikacji umieszczania aktywności zdefiniowanej przez system.
Ograniczenia i zastrzeżenia
- Tylko aplikacja hostująca zadanie może porządkować i umieszczać w zadaniu inne działania jako właściciel głównej aktywności w zadaniu. Jeśli działania obsługujące umieszczanie i podziały są uruchamiane w zadaniu, które należy do innej aplikacji, w przypadku tych aktywności umieszczanie i podziały nie będą działać.
- Działania można uporządkować tylko w ramach jednego zadania. Uruchomienie działania w nowym zadaniu zawsze umieszcza je w nowym rozwiniętym oknie poza istniejącymi podziałami.
- Tylko działania w ramach tego samego procesu można uporządkować i podzielić. Wywołanie zwrotne
SplitInfo
zgłasza tylko działania należące do tego samego procesu, ponieważ nie można dowiedzieć się o działaniach w różnych procesach. - Każda reguła dotycząca pary lub pojedynczej aktywności ma zastosowanie tylko do uruchomień, które miały miejsce po jej zarejestrowaniu. Obecnie nie można aktualizować istniejących podziałów ani ich właściwości wizualnych.
- Konfiguracja filtra pary podzielonej musi być zgodna z intencjami używanymi podczas całkowitego uruchamiania działań. Dopasowywanie ma miejsce w momencie, gdy nowe działanie jest uruchamiane z procesu aplikacji, więc może nie znać nazw komponentów, które są rozpoznawane później w procesie systemowym przy użyciu intencji niejawnych. Jeśli nazwa komponentu nie jest znana w chwili uruchomienia, zamiast niej można użyć symbolu wieloznacznego („*/*”), a filtrowanie można przeprowadzić na podstawie działania intencji.
- Obecnie nie można przenosić aktywności między kontenerami ani do nich i poza nimi po ich utworzeniu. Podziały tworzy biblioteka WindowManager tylko po uruchomieniu nowych aktywności z pasującymi regułami, a podziały są niszczone po zakończeniu ostatniej aktywności w podzielonym kontenerze.
- Działania można uruchomić ponownie po zmianie konfiguracji, więc po utworzeniu lub usunięciu podziału i zmianie granic aktywności aktywność może zostać całkowicie zniszczona przez poprzednią instancję i utworzyć nową. Deweloperzy aplikacji powinni więc ostrożnie uruchamiać nowe działania z wywołań zwrotnych cyklu życia.
- Aby obsługiwać umieszczanie aktywności, urządzenia muszą zawierać interfejs rozszerzeń okien. Prawie wszystkie urządzenia z Androidem 12L (poziom interfejsu API 32) lub nowszym mają ten interfejs. Jednak niektóre urządzenia z dużym ekranem, na których nie można wykonywać wielu działań, nie zawierają interfejsu rozszerzeń okien. Jeśli urządzenie z dużym ekranem nie obsługuje trybu wielu okien, może nie obsługiwać umieszczania aktywności.
Dodatkowe materiały
- Ćwiczenia z programowania – Tworzenie układu ze szczegółami listy z umieszczaniem aktywności
- Ścieżka szkoleniowa – Umieszczanie aktywności