Umieszczanie aktywności optymalizuje aplikacje na urządzeniach z dużym ekranem, rozdzielając okno zadań aplikacji między 2 działania lub 2 wystąpienia tej samej aktywności.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/settings_app.png?authuser=5&hl=pl)
Jeśli aplikacja składa się z wielu aktywności, umieszczanie aktywności umożliwia zwiększenie wygody użytkowników na tabletach, urządzeniach składanych i urządzeniach z ChromeOS.
Umieszczanie aktywności nie wymaga refaktoryzacji kodu. Sposób wyświetlania działań w aplikacji – obok siebie lub w stosunku do siebie – możesz określić, tworząc plik konfiguracji XML lub wywołując interfejs API Jetpack WindowManager.
Obsługa małych ekranów jest obsługiwana automatycznie. Gdy aplikacja działa na urządzeniu z małym ekranem, aktywności układają się jeden na drugim. 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łęzianie.
Umieszczanie aktywności pozwala uwzględnić zmianę orientacji urządzenia i płynnie działa na urządzeniach składanych, układaj je i rozkładaj podczas składania i rozkładania.
Umieszczanie aktywności jest obsługiwane na większości urządzeń z dużymi ekranami z Androidem 12L (poziom interfejsu API 32) lub nowszym.
Okno podziału zadania
Osadzanie aktywności dzieli okno zadań aplikacji na 2 kontenery: główny i dodatkowy. Kontenery zawierają działania uruchomione z głównej aktywności lub innych działań znajdujących się już w kontenerach.
Działania są grupowane w kontenerze dodatkowym po ich uruchomieniu, a kontener dodatkowy jest umieszczony nad głównym kontenerem na małych ekranach. Dzięki temu grupowanie działań i nawigacja wstecz zapewnia kolejność działań już wbudowanych w aplikację.
Umieszczanie aktywności umożliwia prezentowanie aktywności na różne sposoby. Aplikacja może podzielić okno zadania, uruchamiając 2 działania jednocześnie:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_b.png?authuser=5&hl=pl)
Z kolei działanie, które zajmuje całe okno zadania, może spowodować podział, uruchamiając nową aktywność:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_to_a_b.png?authuser=5&hl=pl)
Działania, które są już podzielone i udostępniają okno zadania, mogą uruchamiać inne działania na następujące sposoby:
Obok innej aktywności:
Rysunek 4. Aktywność A rozpoczyna aktywność C na boku względem aktywności B. Przesuń podział na bok, ukrywając poprzednią aktywność główną:
Rysunek 5. Aktywność B rozpoczyna aktywność C na bok i przesuwa podział w bok. Uruchom działanie u góry, czyli w tym samym stosie aktywności:
Rysunek 6. Aktywność B rozpoczyna działanie C bez dodatkowych flag intencji. Uruchom pełne okno aktywności w ramach tego samego zadania:
Rysunek 7. Aktywność A lub B rozpoczyna aktywność C, która wypełnia okno zadania.
Nawigacja wstecz
Różne rodzaje aplikacji mogą mieć różne reguły przechodzenia wstecz w stanie podzielonego okna zadania w zależności od zależności między działaniami lub sposobu wywoływania zdarzenia wstecznego przez użytkowników, np.:
- Łączenie: jeśli działania są ze sobą powiązane i jeden z nich nie powinien być wyświetlany bez drugiego, można skonfigurować nawigację wstecz, aby zakończyć oba.
- Jeśli działania są w pełni niezależne, przejście wstecz po aktywności nie ma wpływu na stan innej aktywności w oknie zadania.
Podczas korzystania z nawigacji przy użyciu przycisków zdarzenie wstecz jest wysyłane do ostatniego zaznaczonego działania. W przypadku nawigacji przy użyciu gestów zdarzenie Wstecz jest wysyłane do działania, w którym został wykonany gest.
Układ z kilkoma panelami
Jetpack WindowManager umożliwia budowanie układu aktywności w kilku panelach na urządzeniach z dużym ekranem 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, lub w układach opartych na widokach (np. SlidingPaneLayout
), mogą zapewnić użytkownikom lepsze wrażenia na dużym ekranie bez refaktoryzacji kodu źródłowego.
Typowym przykładem jest podział według listy. Aby zapewnić wysoką jakość prezentacji, system rozpoczyna działanie związane z listą, a następnie aplikacja natychmiast rozpoczyna aktywność związaną z szczegółami. System przejścia oczekuje na narysowanie obu działań, a następnie wyświetla je razem. Z punktu widzenia użytkownika te 2 działania są uruchamiane jako jedna.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/launcher_to_list-detail.png?authuser=5&hl=pl)
Atrybuty podziału
Możesz określić proporcje okna zadania między podzielonymi kontenerami i rozmieszczenie kontenerów.
W przypadku reguł zdefiniowanych w pliku konfiguracji XML ustaw te atrybuty:
splitRatio
: ustawia proporcje kontenera. Wartość jest liczbą zmiennoprzecinkową w otwartym przedziale czasu (0,0, 1,0).splitLayoutDirection
: określa układ 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 ustawienia lokalnego.
Przykłady znajdziesz w sekcji Konfiguracja XML poniżej.
W przypadku reguł utworzonych przy użyciu interfejsów WindowManager API utwórz obiekt SplitAttributes
za pomocą SplitAttributes.Builder
i wywołaj te metody kreatora:
setSplitType()
: ustawia proporcje podzielonych kontenerów. Prawidłowe argumenty (w tym metodaSplitAttributes.SplitType.ratio()
) znajdziesz w sekcjiSplitAttributes.SplitType
.setLayoutDirection()
: ustawia układ kontenerów. Możliwe wartości znajdziesz na stronieSplitAttributes.LayoutDirection
.
Przykłady znajdziesz poniżej w sekcji WindowManager API.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_b_vs_a_b.png?authuser=5&hl=pl)
Obiekty zastępcze
Aktywności zastępcze to puste działania dodatkowe, które zajmują obszar podziału aktywności. Mają one zostać zastąpione innym działaniem, które zawiera treści. Na przykład aktywność zastępcza może zajmować drugą stronę podziału aktywności w układzie z poziomem listy, dopóki nie zostanie wybrany element z listy. Wtedy działanie zawierające informacje o danym elemencie listy zastępuje obiekt zastępczy.
Domyślnie system wyświetla obiekty zastępcze tylko wtedy, gdy jest wystarczająco dużo miejsca na podział aktywności. Obiekty zastępcze są automatycznie uzupełniane, gdy rozmiar wyświetlania zmieni się na szerokość lub wysokość zbyt małą, aby można było wyświetlić podział. Jeśli jest wystarczająco dużo miejsca, system ponownie uruchamia obiekt zastępczy w stanie inicjowania.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/placeholder_finished_recreated.png?authuser=5&hl=pl)
Atrybut stickyPlaceholder
metody SplitPlaceholderRule
lub setSticky()
SplitPlaceholder.Builder
może jednak 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 z wyświetlacza z jednym panelem zostanie zmniejszony do wyświetlacza z 2 panelami (przykład znajdziesz w sekcji Konfiguracja podziału).
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/placeholder_sticky.png?authuser=5&hl=pl)
Zmiana rozmiaru okna
Zmiany w konfiguracji urządzenia zmniejszają szerokość okna zadań, przez co nie wystarcza na układ z kilkoma panelami (np. gdy składany ekran składa się z dużego ekranu do rozmiaru telefonu lub gdy rozmiar okna aplikacji zmienia się w trybie wielu okien), aktywności niezastępcze w obszarze dodatkowym okna zadania są układane nad działaniami w panelu głównym.
Działania zastępcze są wyświetlane tylko wtedy, gdy szerokość wyświetlacza jest wystarczająca do podziału. Na mniejszych ekranach symbol zastępczy jest automatycznie zamykany. Gdy obszar wyświetlania znów stanie się wystarczająco duży, obiekt zastępczy zostanie odtworzony. (Patrz sekcja Obiekty zastępcze powyżej).
Grupowanie aktywności jest możliwe, ponieważ WindowManager ustawia na Z
Wiele działań w panelu dodatkowym
Działanie B rozpoczyna działanie C bez dodatkowych flag intencji:
w kolejności wykonywania działań w tej kolejności:
W mniejszym oknie zadania aplikacja zmniejsza się do pojedynczego działania z C na górze stosu:
Przejście z powrotem w mniejszym oknie powoduje przejście między aktywnościami nakładanymi na siebie.
Jeśli konfiguracja okna zadań zostanie przywrócona do większego rozmiaru, który może pomieścić wiele okien, działania będą ponownie wyświetlane obok siebie.
Skumulowane podziały
Ćwiczenie B rozpoczyna aktywność C na bok i przesuwa podział w bok:
Efektem jest wykonywanie działań w tej kolejności w tej kolejności:
W mniejszym oknie zadania aplikacja zmniejsza się do pojedynczego działania z C na górze:
Stała orientacja pionowa
Ustawienie w pliku manifestu android:screenOrientation pozwala aplikacjom ograniczać czynności do orientacji pionowej lub poziomej. Aby zwiększyć wygodę użytkowników korzystających z urządzeń z dużym ekranem, takich jak tablety i urządzenia składane, producenci urządzeń (OEM) mogą ignorować żądania orientacji ekranu oraz letterbox, gdy aplikacja ma orientację pionową lub poziomą na wyświetlaczach pionowych.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_letterboxed_portrait_and_landscape.png?authuser=5&hl=pl)
Podobnie po włączeniu umieszczania aktywności na dużych ekranach (szerokość ≥ 600 dp) dostawcy mogą dostosowywać urządzenia do zdjęć poziomych i pionowych. Gdy stały portret powoduje uruchomienie drugiej aktywności, urządzenie może wyświetlić te aktywności obok siebie na wyświetlaczu z 2 panelami.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_letterboxed_portrait_to_a_b.png?authuser=5&hl=pl)
Zawsze dodawaj właściwość android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
do pliku manifestu aplikacji, aby poinformować urządzenia, że aplikacja obsługuje umieszczanie aktywności (zobacz Konfiguracja podziału poniżej). Urządzenia dostosowane do potrzeb OEM mogą wtedy określić, czy mają być używane stałe pasy pionowe.
Podziel konfigurację
Reguły podziału służą do konfigurowania podziałów aktywności. Reguły podziału definiuje się w pliku konfiguracji XML lub przez wywołania interfejsu API Jetpack WindowManager.
W obu przypadkach aplikacja musi mieć dostęp do biblioteki WindowManager i musi informować system, że aplikacja ma zaimplementowane umieszczanie aktywności.
Wykonaj te czynności:
Dodaj najnowszą zależność biblioteki WindowManager 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 zaimplementowane umieszczanie aktywności.
Dodaj właściwość
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
do elementu <application> w pliku manifestu aplikacji i ustaw wartość „true”, na przykład:<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 usłudze WindowManager w wersji 1.1.0-alpha06 i nowszych podziały osadzonych aktywności są wyłączone, chyba że właściwość została dodana do pliku manifestu i ma wartość true (prawda).
Oprócz tego producenci urządzeń używają tego ustawienia do włączania niestandardowych funkcji w aplikacjach, które obsługują umieszczanie aktywności. Na przykład urządzenia mogą wyświetlać aktywność w trybie poziomym tylko w orientacji pionowej na wyświetlaczu poziomym, aby ukierunkować aktywność na potrzeby przejścia na układ z 2 panelami po rozpoczęciu drugiej aktywności (patrz Stała orientacja pionowa).
Konfiguracja XML
Aby utworzyć opartą na XML implementację wektora dystrybucyjnego aktywności, wykonaj te czynności:
Utwórz plik zasobów XML, który:
- Definiuje działania o wspólnym podziale
- Konfiguruje opcje 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 w systemie. Biblioteka startowaInitializer
Jetpacka udostępnia plik XMLRuleController
podczas uruchamiania aplikacji, dzięki czemu reguły są stosowane po rozpoczęciu dowolnych działań.Aby utworzyć inicjator, wykonaj te czynności:
Dodaj najnowszą zależność biblioteki startowej 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 usłudze
RuleController
, przekazując identyfikator pliku konfiguracji XML (main_split_config.xml
) do metodyRuleController.parseRules()
.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 dla definicji reguł.
Dodaj
androidx.startup.InitializationProvider
do pliku manifestu aplikacji jako<provider>
. Dodaj odwołanie 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()
aplikacji. Z tego powodu reguły podziału obowiązują po rozpoczęciu głównej aktywności w aplikacji.
Interfejs API WindowManager
Umieszczanie aktywności możesz zaimplementować automatycznie za pomocą kilku wywołań interfejsu API. Wywołuj metodę onCreate()
w podklasie Application
, aby mieć pewność, że reguły zaczną obowiązywać przed uruchomieniem działań.
Aby automatycznie utworzyć podział aktywności:
Utwórz regułę podziału:
Utwórz
SplitPairFilter
wskazujący działania, które należą do tego samego 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 );
Dodaj filtr do zestawu filtrów:
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
Utwórz atrybuty układu na potrzeby 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 przydzielania dostępnego obszaru do każdego kontenera aktywności. Typ podziału współczynnika określa odsetek 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 nawzajem – kontenera głównego.
Tworzenie
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 par podzielonych, które określają, kiedy zastosować regułę, identyfikując aktywności o takim samym podziale.setDefaultSplitAttributes
: stosuje atrybuty układu do reguły.setMinWidthDp
: ustawia minimalną szerokość wyświetlacza (w pikselach niezależnych od gęstości, dp), która umożliwia podział danych.setMinSmallestWidthDp
: określa minimalną wartość (w dp), jaką musi mieć mniejsza z tych 2 wymiarów wyświetlania, aby umożliwić podział bez względu na orientację urządzenia.setMaxAspectRatioInPortrait
: ustawia maksymalny format obrazu (wysokość:szerokość) w orientacji pionowej, przy którym są wyświetlane podziały aktywności. Jeśli format obrazu pionowego przekracza maksymalny współczynnik proporcji, podziały są wyłączone niezależnie od szerokości ekranu. Uwaga: wartość domyślna to 1,4, co na większości tabletów zajmuje całe okno zadania w orientacji pionowej. Zobacz teżSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
isetMaxAspectRatioInLandscape
. Wartość domyślna w przypadku orientacji poziomej toALWAYS_ALLOW
.setFinishPrimaryWithSecondary
: określa, jak ukończenie wszystkich działań w kontenerze dodatkowym wpływa na działania w kontenerze głównym.NEVER
wskazuje, że system nie powinien kończyć działań głównych po zakończeniu wszystkich działań w kontenerze dodatkowym (patrz Kończenie działań).setFinishSecondaryWithPrimary
: określa, jak ukończenie wszystkich działań w kontenerze głównym wpływa na działania w kontenerze dodatkowym.ALWAYS
oznacza, że system powinien zawsze kończyć działania w kontenerze dodatkowym po zakończeniu wszystkich działań w kontenerze głównym (patrz Kończenie działań).setClearTop
: określa, czy wszystkie działania w kontenerze dodatkowym zostaną zakończone po uruchomieniu w kontenerze nowej aktywności. Wartość Fałsz oznacza, że nowe działania są nakładane na działania znajdujące się już w kontenerze dodatkowym.
Pobierz instancję usługi WindowManager
RuleController
i dodaj do niej regułę:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
Utwórz obiekt zastępczy kontenera dodatkowego, gdy treść jest niedostępna:
Utwórz obiekt
ActivityFilter
identyfikujący aktywność, z którą obiekt zastępczy ma wspólny podział okien zadań:Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
Dodaj filtr do zestawu filtrów:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
Tworzenie
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 aktywności, z którymi powiązana jest aktywność zastępcza.Intent
: określa uruchomienie działania związanego z obiektem zastępczym.setDefaultSplitAttributes
: stosuje atrybuty układu do reguły.setMinWidthDp
: ustawia minimalną szerokość wyświetlacza (w pikselach niezależnych od gęstości, dp), która umożliwia podział danych.setMinSmallestWidthDp
: określa minimalną wartość (w dp), jaką musi być mniejszy z 2 wymiarów wyświetlacza, by umożliwić podział bez względu na orientację urządzenia.setMaxAspectRatioInPortrait
: ustawia maksymalny format obrazu (wysokość:szerokość) w orientacji pionowej, przy którym są wyświetlane podziały aktywności. Uwaga: wartość domyślna to 1,4, co na większości tabletów powoduje, że zadania wypełniają okno zadania w orientacji pionowej. Zobacz teżSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
isetMaxAspectRatioInLandscape
. Wartość domyślna w przypadku orientacji poziomej toALWAYS_ALLOW
.setFinishPrimaryWithPlaceholder
: określa, jak zakończenie działania obiektu zastępczego wpływa na działania w kontenerze głównym. ZAWSZE wskazuje, że system powinien zawsze kończyć działania w kontenerze głównym po zakończeniu działania symbolu zastępczego (patrz Kończenie działań).setSticky
: określa, czy aktywność zastępcza pojawia się na stosu aktywności na małych ekranach, gdy obiekt zastępczy pojawi się po raz pierwszy w części podziału z wystarczającą minimalną szerokością.
Dodaj regułę do interfejsu WindowManager
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 obiekt
ActivityFilter
wskazujący aktywność, która zawsze powinna zajmować cały obszar wyświetlania zadań:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
Dodaj filtr do zestawu filtrów:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
Tworzenie
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, które określają, kiedy zastosować regułę, identyfikując aktywności, które chcesz wykluczyć z podziałów.setAlwaysExpand
: określa, czy aktywność powinna wypełniać całe okno zadania.
Dodaj regułę do interfejsu WindowManager
RuleController
:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
Umieszczanie treści z wielu aplikacji
Na Androidzie 13 (poziom interfejsu API 33) i nowszym aplikacje można umieszczać aktywności z innych aplikacji. Umieszczanie aktywności w różnych aplikacjach lub przez UID umożliwia wizualną integrację działań z wielu aplikacji na Androida. System wyświetla na ekranie obok siebie lub u góry i u dołu aktywność aplikacji hostującej i umieszczoną na niej aktywność z innej aplikacji, tak jak w przypadku umieszczania aktywności w pojedynczej aplikacji.
Na przykład aplikacja Ustawienia może umieścić aktywność selektora tapet z aplikacji TapPicker:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/wallpaper_activity_embedded_in_settings_app.png?authuser=5&hl=pl)
Model zaufania
Procesy hosta, w których umieszczone są działania z innych aplikacji, mogą na nowo zdefiniować sposób, w jaki te działania są prezentowane, w tym zmieniać rozmiar, położenie, przycinanie i przezroczystość. Złośliwe hosty mogą korzystać z tej funkcji, aby wprowadzać użytkowników w błąd i tworzyć clickjacking lub inne ataki mające na celu korygowanie działania interfejsu użytkownika.
Aby zapobiec niewłaściwemu używaniu umieszczania danych o aktywności w różnych aplikacjach, Android wymaga, aby aplikacje zezwalały na umieszczanie takich danych. Aplikacje mogą oznaczać hosty jako zaufane lub niezaufane.
Zaufane hosty
Aby umożliwić innym aplikacjom umieszczanie aktywności z Twojej aplikacji i pełną kontrolę nad ich prezentacją, podaj certyfikat SHA-256 aplikacji hosta w atrybucie android:knownActivityEmbeddingCerts
elementów <activity>
lub <application>
w pliku manifestu aplikacji.
Ustaw wartość android:knownActivityEmbeddingCerts
jako ciąg znaków:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
lub, aby określić wiele certyfikatów, tablica ciągów znaków:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
odwołujący się do zasobu takiego jak ten:
<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 rozdzielających. Więcej informacji znajdziesz w artykułach na temat wygenerowania raportu podpisywania i uwierzytelniania 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 pliku manifestu aplikacji, na przykład:
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
Domyślna wartość tego atrybutu to „false”, co uniemożliwia umieszczanie aktywności w różnych aplikacjach.
Uwierzytelnianie niestandardowe
Aby ograniczyć ryzyko związane z umieszczaniem niezaufanych aktywności, utwórz niestandardowy mechanizm uwierzytelniania, który zweryfikuje tożsamość hosta. Jeśli znasz certyfikaty hosta, użyj biblioteki androidx.security.app.authenticator
do uwierzytelnienia. Jeśli host uwierzytelni się po umieszczeniu Twojej aktywności, możesz wyświetlić rzeczywiste treści. W przeciwnym razie możesz poinformować użytkownika, że 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 dotyczące minimalnego rozmiaru
Do umieszczonych aktywności system Android stosuje minimalną wysokość i szerokość określoną w elemencie <layout>
manifestu aplikacji. Jeśli aplikacja nie określa minimalnej wysokości i szerokości, obowiązują systemowe wartości domyślne (sw220dp
).
Jeśli host spróbuje zmienić rozmiar umieszczonego kontenera do rozmiaru mniejszego niż minimalny, ten kontener zostanie rozwinięty i zajmie całe granice zadania.
<alias-aktywności>
Aby umieszczanie zaufanych lub niezaufanych aktywności działało z elementem <activity-alias>
, do aktywności docelowej, a nie aliasu, należy zastosować android:knownActivityEmbeddingCerts
lub android:allowUntrustedActivityEmbedding
. Zasada sprawdzająca bezpieczeństwo serwera systemowego jest oparta na flagach ustawionych w środowisku docelowym, a nie na aliasie.
Aplikacja hosta
Aplikacje hostujące implementują osadzanie aktywności w różnych aplikacjach w taki sam sposób, w jaki implementują osadzanie aktywności w pojedynczej aplikacji. Obiekty SplitPairRule
i SplitPairFilter
lub ActivityRule
i ActivityFilter
określają umieszczone działania i podziały okien zadań. Reguły podziału definiuje się statycznie w formacie XML lub w czasie działania za pomocą wywołań interfejsu API Jetpack WindowManager.
Jeśli aplikacja hosta próbuje umieścić działanie, które nie ma włączonej opcji umieszczania w różnych aplikacjach, aktywność ta zajmuje cały obszar zadania. W związku z tym aplikacje hosta muszą wiedzieć, czy działania docelowe umożliwiają umieszczanie w różnych aplikacjach.
Jeśli osadzona aktywność rozpoczyna nowe działanie w ramach tego samego zadania, a nowe działanie nie ma włączonej opcji umieszczania w różnych aplikacjach, aktywność zajmuje cały zakres zadania, zamiast nakładać się na działanie w umieszczonym kontenerze.
Aplikacja hosta może osadzać własne działania bez ograniczeń, o ile działania są uruchamiane w tym samym zadaniu.
Przykłady podziału
Podziel od całego okna
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_to_a_b.png?authuser=5&hl=pl)
Refaktoryzacja nie jest wymagana. Konfigurację podziału możesz zdefiniować statycznie lub w czasie działania, a następnie wywoływać metodę Context#startActivity()
bez dodatkowych parametrów.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Podziel domyślnie
Gdy strona docelowa aplikacji jest zaprojektowana na 2 kontenery na dużych ekranach, największą wygodę użytkowników zapewnia utworzenie i prezentowanie obu działań jednocześnie. Treść może być jednak niedostępna w przypadku dodatkowego kontenera podziału, dopóki użytkownik nie wejdzie w interakcję z działaniem w kontenerze głównym (np. nie wybierze elementu z menu nawigacyjnego). Działanie tymczasowe może wypełnić lukę, dopóki jej treść nie wyświetli się w dodatkowym kontenerze podziału (patrz Obiekty zastępcze powyżej).
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/launcher_main_placeholder.png?authuser=5&hl=pl)
Aby to zrobić, utwórz obiekt zastępczy i powiąż go z głównym działaniem:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
Podział precyzyjnych linków
Gdy aplikacja otrzyma intencję, działanie docelowe może być wyświetlane jako dodatkowa część podziału aktywności; na przykład żądanie wyświetlenia ekranu ze szczegółami z informacjami o elemencie z listy. Na małych ekranach szczegóły są wyświetlane w pełnym oknie zadań, a na większych urządzeniach – obok listy.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/deep_link_split.png?authuser=5&hl=pl)
Prośba o uruchomienie powinna być kierowana do głównego działania, a docelowe działanie szczegółowe powinno zostać uruchomione w części. System automatycznie wybierze właściwą prezentację – ułożone w stos lub obok siebie – na podstawie dostępnej szerokości wyświetlania.
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ć jedynym działaniem, które powinno być dostępne dla użytkownika w stosie nawigacji wstecz. Lepiej nie zamykać działania szczegółów i pozostawić tylko działania głównego:
Zamiast tego możesz zakończyć obie aktywności w tym samym czasie, używając atrybutu finishPrimaryWithSecondary
:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
Patrz sekcja Atrybuty konfiguracji poniżej.
Wiele aktywności w podzielonych kontenerach
Gromadzenie wielu aktywności w podzielnym kontenerze umożliwia użytkownikom dostęp do szczegółowych treści. Na przykład podział według szczegółów listy może sprawić, że użytkownik będzie musiał przejść do sekcji szczegółów podrzędnych, ale zachować główne działanie:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/list-detail_to_list-sub-detail.png?authuser=5&hl=pl)
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
Działanie podrzędne jest umieszczone nad działaniem szczegółów i jest ukrywane:
Użytkownik może potem wrócić do poprzedniego poziomu szczegółów, wracając do stosu:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/list-sub-detail_to_list-detail.png?authuser=5&hl=pl)
Układanie działań na siebie to domyślne zachowanie, gdy działania są uruchamiane z poziomu aktywności w tym samym kontenerze dodatkowym. Działania uruchomione z kontenera głównego w ramach aktywnego podziału również trafiają do kontenera dodatkowego u góry stosu aktywności.
Działania w nowym zadaniu
Gdy działania w podzielonym oknie zadania rozpoczynają działania w nowym zadaniu, nowe zadanie jest oddzielone od zadania, które obejmuje podział, i wyświetla się w pełnym oknie. Ekran Ostatnie pokazuje 2 zadania: zadanie w podziale i nowe zadanie.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/task_1_to_task_2.png?authuser=5&hl=pl)
Zastępowanie aktywności
Działania można zastępować w stosie dodatkowych kontenerów, np. gdy działanie główne jest używane do nawigacji najwyższego poziomu, a działanie dodatkowe jest wybranym miejscem docelowym. Każda opcja nawigacji najwyższego poziomu powinna rozpoczynać nowe działanie w kontenerze dodatkowym i usunąć dotychczasowe.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/menu_screen_a_to_menu_screen_b.png?authuser=5&hl=pl)
Jeśli aplikacja nie zakończy działania w kontenerze dodatkowym po zmianie wyboru nawigacji, nawigacja wstecz może być myląca, gdy podział jest zwinięty (po złożeniu urządzenia). Jeśli na przykład w panelu głównym masz menu, a ekrany A i B w panelu dodatkowym, gdy użytkownik złożysz telefon, B znajduje się na górze A, a A – u góry menu. Gdy użytkownik wróci z poziomu B, zamiast menu pojawi się A.
W takich przypadkach ekran A musi zostać usunięty z tylnego stosu.
Domyślnym zachowaniem przy uruchamianiu z boku w nowym kontenerze w stosunku do istniejącego podziału jest umieszczenie nowych kontenerów dodatkowych na początku i zachowanie starych kontenerów. Możesz skonfigurować podziały, aby wyczyścić poprzednie kontenery dodatkowe za pomocą funkcji 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ć tego samego działania dodatkowego, a z działania głównego (menu) wysyłać nowe intencje, które rozwiązują problem z tą samą instancją, ale wywołują stan lub aktualizację interfejsu użytkownika w kontenerze dodatkowym.
Wiele podziałów
Aplikacje mogą zapewniać głęboką nawigację wielopoziomową poprzez uruchamianie dodatkowych działań z boku strony.
Gdy działanie w kontenerze dodatkowym uruchamia nową aktywność z boku, w miejsce istniejącego podziału tworzony jest nowy podział.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/a_b_to_b_c.png?authuser=5&hl=pl)
Stos tylny zawiera wszystkie otwarte wcześniej działania, więc użytkownicy mogą przejść do podziału A/B po skończeniu pracy C.
Aby utworzyć nowy podział, uruchom nową aktywność z boku istniejącego kontenera dodatkowego. Zadeklaruj konfiguracje dla przydziałów A/B i B/C i uruchom działanie C, normalnie z poziomu 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 działania w aplikacji mogą mieć elementy interfejsu, które pełnią tę samą funkcję, np. element sterujący, który otwiera okno z ustawieniami konta.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/main_detail_with_ui_element.png?authuser=5&hl=pl)
Jeśli 2 działania, które mają wspólny element interfejsu, są częścią podziału, pokazanie tego elementu w obu działaniach może powodować nadmiarowość i dezorientację.
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/main_detail_with_ui_element_duplicated.png?authuser=5&hl=pl)
Aby dowiedzieć się, kiedy działania są podzielone, sprawdź proces SplitController.splitInfoList
lub zarejestruj detektor w SplitControllerCallbackAdapter
, aby sprawdzić zmiany 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 o używaniu współrzędnych Kotlina z komponentami dostosowanymi do cyklu życia).
Wywołania zwrotne mogą być wykonywane w dowolnym stanie cyklu życia, w tym po zatrzymaniu działania. Słuchacze powinni zwykle być zarejestrowani w kraju onStart()
, a niezarejestrowani w onStop()
.
Widok modalny z pełnym oknem
Niektóre działania blokują użytkownikom możliwość interakcji z aplikacją do czasu wykonania określonego działania. Są to na przykład działania na ekranie logowania, ekran potwierdzenia zgodności z zasadami lub komunikat o błędzie. Aktywności modalne powinny być zapobiegane wyświetlaniu się w podziale.
Za pomocą konfiguracji rozwijania działanie może być wymuszone zawsze wypełnienie okna zadania:
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Kończenie działań
Użytkownicy mogą dokończyć czynności po obu stronach podziału, przesuwając palcem od krawędzi wyświetlacza:
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/swipe_gesture_finish_b.png?authuser=5&hl=pl)
![](https://developer.android.google.cn/static/images/guide/topics/large-screens/activity-embedding/swipe_gesture_finish_a.png?authuser=5&hl=pl)
Jeśli urządzenie zostało skonfigurowane do używania przycisku Wstecz zamiast nawigacji przy użyciu gestów, dane wejściowe są wysyłane do zaznaczonego działania, czyli do aktywności, która została dotknięta lub uruchomiona jako ostatnia.
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 pary, aby określić, jak zakończenie wszystkich działań po jednej stronie podziału wpływa na aktywności po drugiej stronie podziału. Te atrybuty:
window:finishPrimaryWithSecondary
– jak zakończenie wszystkich działań w kontenerze dodatkowym wpływa na działania w kontenerze głównym.window: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 kontenerze.never
– nigdy nie dokończ działań w powiązanym kontenerze.adjacent
– dokończ działania w powiązanym kontenerze, gdy 2 kontenery są wyświetlane obok siebie, ale nie wtedy, gdy dwa kontenery są ustawione w stosie
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 wszystkie działania w jednym kontenerze w ramach podzielonego zakończenia zajmują cały kontener, pozostały kontener zajmuje całe okno:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Wspólne kończenie działań
Automatycznie dokończ działania w kontenerze głównym, gdy wszystkie działania w kontenerze dodatkowym zostaną zakończone:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Automatycznie dokończ działania w kontenerze dodatkowym, gdy wszystkie działania w kontenerze głównym zostaną zakończone:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Zakończ działania razem, gdy zakończą się wszystkie działania 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 wiele aktywności znajduje się w podzielonym kontenerze, wykonanie jednej z nich na dole stosu nie spowoduje automatycznie zakończenia aktywności u góry.
Jeśli na przykład w kontenerze dodatkowym znajdują się 2 działania, nadrzędne są działania C nad nimi:
a konfigurację podziału definiuje się przez konfigurację działań A i B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
do zakończenia głównej aktywności zachowuje podział.
Zakończenie działania dolnego (głównego) kontenera dodatkowego nie powoduje usunięcia znajdujących się na nim działań, więc zachowuje się podział.
Wszelkie dodatkowe reguły dotyczące kończenia działań razem, np. zakończenie aktywności dodatkowej z aktywnością główną, również są realizowane:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Gdy podział zostanie skonfigurowany tak, aby zakończyć razem: główną i dodatkową:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Zmiana 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, zakończ aktywność dodatkową w tym przydziale i uruchom jeszcze raz z nową konfiguracją.
Wyodrębnij aktywność z podzielonego do pełnego okna
Utwórz nową konfigurację, która wyświetla pełne okno aktywności bocznej, a następnie ponownie uruchom aktywność z intencją kierującą do tej samej instancji.
Sprawdzanie obsługi podziału 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 na początku stosu aktywności (zgodnie z modelem umieszczania aktywności bez aktywności).
Zapobiegaj zastąpieniu systemu
Producenci urządzeń z Androidem (producentom oryginalnego sprzętu lub OEM) mogą wdrożyć umieszczanie aktywności jako funkcję systemu urządzeń. System określa reguły podziału dla aplikacji o wielu działaniach, które zastępują zachowanie w przypadku wyświetlania okien. Zastąpienie przez system wymusza na aplikacjach o wielu aktywnościach zdefiniowany przez system tryb umieszczania aktywności.
Umieszczanie aktywności w systemie może poprawić prezentację aplikacji za pomocą wielu paneli, takich jak szczegóły listy, bez wprowadzania zmian w aplikacji. Umieszczanie aktywności w systemie może jednak powodować nieprawidłowe układy aplikacji, błędy lub konflikty z implementowanymi przez aplikację umieszczaniem aktywności.
Aplikacja może zezwolić na umieszczanie aktywności systemu lub na to zezwolić, ustawiając właściwość w pliku manifestu aplikacji, 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 Jetpack WindowManager WindowWłaściwości.
Ustaw wartość false
, jeśli Twoja aplikacja stosuje umieszczanie aktywności lub jeśli chcesz w inny sposób uniemożliwić systemowi stosowanie w niej reguł tego typu. Ustaw wartość true
, aby system mógł zastosować w Twojej aplikacji zdefiniowane przez system umieszczanie aktywności.
Ograniczenia, ograniczenia i zastrzeżenia
- Tylko aplikacja hostująca zadanie, określona jako właściciel działania głównego w zadaniu, może organizować i umieszczać w zadaniu inne działania. Jeśli działania obsługujące umieszczanie i podziały są uruchamiane w zadaniu należącym do innej aplikacji, w przypadku tych działań umieszczanie i podziały nie będą działać.
- Czynności można uporządkować tylko w ramach jednego zadania. Uruchomienie działania w nowym zadaniu zawsze powoduje umieszczenie go w nowym, rozszerzonym oknie poza istniejącymi podziałami.
- Tylko działania w ramach tego samego procesu mogą być porządkowane i podzielone na segmenty. Wywołanie zwrotne
SplitInfo
zgłasza tylko działania, które należą do tego samego procesu, ponieważ nie ma możliwości sprawdzenia działań w różnych procesach. - Każda para lub pojedyncza reguła związana z aktywnością ma zastosowanie tylko do uruchomień działań, które nastąpiły po zarejestrowaniu reguły. Obecnie nie można zaktualizować 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 następuje w momencie, gdy rozpoczyna się nowe działanie w ramach procesu aplikacji, więc może nie wiedzieć o nazwach komponentów, które są rozpoznawane w dalszej części procesu systemowego przy użyciu intencji niejawnych. Jeśli w momencie uruchomienia aplikacji nie jest znana nazwa komponentu, można zamiast niej użyć symbolu wieloznacznego („*/*”), a filtrowanie można wykonać na podstawie działania intencji.
- Obecnie nie ma możliwości przenoszenia działań między kontenerami ani do i z przydziałów po ich utworzeniu. Podziały są tworzone przez bibliotekę WindowManager tylko po uruchomieniu nowych działań z pasującymi regułami, a podziały są niszczone po zakończeniu ostatniej aktywności w podzielonym kontenerze.
- Działania można uruchamiać ponownie po zmianie konfiguracji, więc po utworzeniu lub usunięciu podziału oraz zmianie granic aktywności może dojść do całkowitego zniszczenia poprzedniej instancji i utworzenia nowej. W związku z tym deweloperzy aplikacji powinni zachować ostrożność podczas uruchamiania nowych działań z wywołań zwrotnych cyklu życia.
- Urządzenia muszą mieć interfejs rozszerzeń okien, aby obsługiwać umieszczanie aktywności. Prawie wszystkie urządzenia z dużym ekranem z Androidem 12L (poziom interfejsu API 32) lub nowszym mają interfejs. Jednak niektóre urządzenia z dużym ekranem, które nie obsługują wielu aktywności, nie mają 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
- Ćwiczenie z programowania – Tworzenie układu z opisem listy z wykorzystaniem umieszczania aktywności i interfejsu Material Design
- Ścieżka szkoleniowa – Umieszczanie modułów