Umieszczanie aktywności

Umieszczanie aktywności optymalizuje aplikacje na urządzeniach z dużym ekranem, dzieląc między 2 działaniami lub 2 instancjami tego samego działania.

Rysunek 1. Aplikacja Ustawienia z aktywnościami obok siebie.

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 jest na i urządzenia z małym ekranem, aktywności są nakładane jeden na drugi. Wł. na dużych ekranach, aktywności są wyświetlane obok siebie. System określa prezentacji na podstawie utworzonej przez Ciebie konfiguracji – bez rozgałęziania niezbędna jest logika.

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 drugorzędne. Kontenery zawierają działania uruchomione z głównej aktywności lub z innych aktywności znajdujących się już w kontenerach.

Działania są grupowane w kontenerze dodatkowym w miarę ich uruchamiania. kontener dodatkowy jest umieszczony nad głównym kontenerem Dzięki temu dodawanie aktywności i nawigacja wstecz uporządkowanie aktywności 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 aktywności obok siebie jednocześnie:

Rysunek 2. Dwie aktywności obok siebie.

Działanie, które zajmuje całe okno zadania, może też spowodować podział według rozpoczyna nowe działanie obok:

Rysunek 3. Aktywność A rozpoczyna aktywność B z boku.
.

Działania, które są już podzielone i udostępniają okno zadania, mogą zostać uruchomione inne rodzaje aktywności:

  • 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. Ćwiczenie B rozpoczyna aktywność C na bok i przesuwa dzielą się na bok.
  • Uruchamianie aktywności u góry. czyli w ramach tego samego stosu 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 w oknie zadania.

Nawigacja wstecz

Różne rodzaje aplikacji mogą mieć różne reguły przechodzenia wstecz podziału okna zadań w zależności od zależności między działaniami lub jak użytkownicy wywołują zdarzenie wstecz, na przykład:

  • Łą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 dokończyć oba.
  • Jeśli czynności są w pełni niezależne: jeśli czynności są w pełni niezależne, przejdź wstecz po działanie nie ma wpływu na stan innej aktywności w oknie zadania.

Gdy używasz przycisku, zdarzenie cofania jest wysyłane do ostatnio zaznaczonego działania nawigacji. Przy nawigowaniu przy użyciu gestów zdarzenie wstecz jest wysyłane do aktywność, podczas której 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.

Rysunek 8. 2 aktywności rozpoczęte jednocześnie w kilku panelach układ.

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 prawej
    • rtl: od prawej do lewej
    • locale: wartość ltr lub rtl 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:

Przykłady znajdziesz poniżej w sekcji WindowManager API.

Rysunek 9. 2 części aktywności ułożone od lewej do prawej, ale z różnymi współczynnikami podziału.

Obiekty zastępcze

Aktywności zastępcze to puste aktywności dodatkowe, które zajmują obszar podział aktywności. Z czasem mają zostać zastąpione innym aktywność, która zawiera treści. Może to być na przykład aktywność zastępcza zajmują drugą stronę podziału aktywności w układzie z szczegółami listy do czasu element z listy zostanie wybrany, co spowoduje, że działanie zawierające szczegółowe informacje o wybranym elemencie listy zastępują obiekt zastępczy.

Domyślnie system wyświetla obiekty zastępcze tylko wtedy, gdy jest wystarczająco dużo miejsca na podział aktywności. Symbole zastępcze są automatycznie uzupełniane, gdy rozmiar wyświetlacza 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.

Rysunek 10. Składanie i rozkładanie urządzenia. Obiekt zastępczy a następnie zakończyć działanie i odtworzyć je w przypadku zmiany rozmiaru wyświetlacza.

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 zostaje zmieniony z wyświetlacza z 2 panelami do rozmiaru ekranu z jednym panelem (przykład znajdziesz w sekcji Konfiguracja podziału).

Rysunek 11. Składanie i rozkładanie urządzenia. Obiekt zastępczy aktywność jest zapamiętywana.

Zmiana rozmiaru okna

Zmiana konfiguracji urządzenia powoduje zmniejszenie szerokości okna zadań, aby nie jest dostatecznie duży, aby zmieścić się w układzie z kilkoma panelami (np. gdy duży ekran jest duży Urządzenie składa się, gdy składa się zmienionych w trybie wielu okien), aktywności niezastępczych w obszarze dodatkowym. okno zadania znajduje się nad działaniami w panel.

Aktywności zastępcze są wyświetlane tylko wtedy, gdy mają wystarczającą szerokość obrazu podzielić dane. Na mniejszych ekranach symbol zastępczy jest automatycznie zamykany. Kiedy obszar wyświetlania ponownie stanie się dostatecznie duży, i zostanie utworzony obiekt zastępczy. (Patrz sekcja Obiekty zastępcze powyżej).

Grupowanie aktywności jest możliwe, ponieważ WindowManager ustawia kolejność elementów na osi Z w panelu dodatkowym nad działaniami w panelu głównym.

Wiele działań w panelu dodatkowym

Działanie B rozpoczyna działanie C bez dodatkowych flag intencji:

Podział aktywności obejmujący aktywności A, B i C z nałożonymi aktywnościami C
          góra B.

w kolejności wykonywania działań w tej kolejności:

Dodatkowy stos aktywności zawierający aktywność C nałożony na działanie B.
          Stos dodatkowy jest ułożony na stosie aktywności podstawowych
          zawierające aktywność A.

W mniejszym oknie zadania aplikacja zmniejsza się do pojedynczego działania, z C na górze stosu:

Małe okno, w którym widać tylko aktywność C.

Przejście z powrotem w mniejszym oknie powoduje przejście między nałożonymi aktywnościami jedna nad drugą.

Jeśli konfiguracja okna zadań zostanie przywrócona do większego rozmiaru, i mieści się w wielu panelach, aktywności są ponownie wyświetlane obok siebie.

Skumulowane podziały

Ćwiczenie B rozpoczyna aktywność C na bok i przesuwa podział w bok:

Okno zadania z działaniami A i B oraz zadaniami B i C.

Efektem jest wykonywanie działań w tej kolejności w tej kolejności:

Aktywności A, B i C w jednym stosie. Ćwiczenia są wyświetlane w stosie
          w następującej kolejności od góry do dołu: C, B, A.

W mniejszym oknie zadania aplikacja zmniejsza się do pojedynczego działania z C na górze:

Małe okno, w którym widać tylko aktywność C.

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 i letterbox, gdy aplikacja ma orientację pionową lub poziomą na wyświetlaczach pionowych.

Rysunek 12. Ćwiczenia z czarnymi pasami: obraz ustawiony w orientacji pionowej na urządzeniu poziomym (po lewej) lub w orientacji poziomej na urządzeniu pionowym (po prawej).

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.

Rysunek 13. Ćwiczenie A o stałej orientacji pionowej rozpoczyna aktywność B z boku.

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:

  1. 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.

  2. Poinformuj system, że w Twojej aplikacji jest zaimplementowane umieszczanie aktywności.

    Dodaj właściwość android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED do <application> pliku manifestu aplikacji i ustaw wartość 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 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:

  1. Utwórz plik zasobów XML, który:

    • Definiuje działania o wspólnym podziale
    • Konfiguruje opcje podziału
    • Tworzy obiekt placeholder 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>
    
  2. Utwórz inicjator.

    Komponent WindowManager RuleController analizuje plik konfiguracji XML i udostępnia reguły w systemie. Biblioteka startowa Initializer Jetpacka udostępnia plik XML RuleController podczas uruchamiania aplikacji, dzięki czemu reguły są stosowane po rozpoczęciu dowolnych działań.

    Aby utworzyć inicjator, wykonaj te czynności:

    1. Dodaj najnowszą zależność biblioteki Jetpack Startup do pliku build.gradle na poziomie modułu, na przykład:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. 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 metody RuleController.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();
        }
      }
      
  3. Utwórz dostawcę treści dla definicji reguł.

    Dodaj androidx.startup.InitializationProvider do pliku manifestu aplikacji jako <provider>. Dodaj odwołanie do implementacji inicjatora RuleController (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 inicjuje SplitInitializer przed wywołaniem metody onCreate() 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:

  1. Utwórz regułę podziału:

    1. 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
      );
      
    2. Dodaj filtr do zestawu filtrów:

      Kotlin

      val filterSet = setOf(splitPairFilter)
      

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      
    3. 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 część 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.
    4. 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 i setMaxAspectRatioInLandscape. Wartość domyślna w przypadku orientacji poziomej to ALWAYS_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.
    5. 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);
      
  2. Utwórz obiekt zastępczy kontenera dodatkowego, gdy treść jest niedostępna:

    1. 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
      );
      
    2. Dodaj filtr do zestawu filtrów:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
      

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
      
    3. 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 i setMaxAspectRatioInLandscape. Wartość domyślna w przypadku orientacji poziomej to ALWAYS_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ą.
    4. Dodaj regułę do interfejsu WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)
      

      Java

      ruleController.addRule(splitPlaceholderRule);
      
  3. Określ działania, które nigdy nie powinny być częścią podziału:

    1. 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
      );
      
    2. Dodaj filtr do zestawu filtrów:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)
      

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
      
    3. 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.
    4. 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:

Rysunek 14. Aplikacja Ustawienia (menu po lewej) z selektorem tapety jako aktywnością umieszczoną (po prawej).

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 ich aktywności. 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

Rysunek 15. Aktywność A rozpoczyna aktywność B z boku.

Refaktoryzacja nie jest wymagana. Możesz zdefiniować konfigurację dla podziału statycznie lub w czasie działania, a potem wywołaj Context#startActivity() bez dodatkowych parametrów.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Podziel domyślnie

Gdy strona docelowa aplikacji jest zaprojektowana tak, aby dzieliła się na dwie części na dużych ekranach, zapewnia wygodę użytkownikom, gdy są tworzone i prezentowane jednocześnie. Treści te mogą jednak dostępne dla kontenera dodatkowego podziału, dopóki użytkownik nie wejdzie w interakcję z działaniem w kontenerze głównym (np. użytkownik wybiera w menu nawigacyjnym). Działanie tymczasowe może wypełnić lukę do momentu, może być wyświetlana w dodatkowym kontenerze podziału (patrz Obiekty zastępcze powyżej).

Rysunek 16. Podział utworzony przez otwarcie 2 zadań jednocześnie. Jedna aktywność jest obiektem zastępczym.

Aby to zrobić, utwórz zmienną i powiąż ją z obiektem zastępczym z główną aktywnością:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

Gdy aplikacja otrzymuje intencję, działanie docelowe może być wyświetlane jako dodatkowa część podziału aktywności; np. żądanie wyświetlenia szczegółów, ekran z informacjami o elemencie z listy. Na małych ekranach szczegóły są wyświetlane w pełnym oknie zadania; na większych urządzeniach.

Rysunek 17. szczegółową aktywność związaną z precyzyjnymi linkami wyświetlanymi na małym ekranie, ale razem z działaniami na dużym ekranie.

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ć jedyną dostępną aktywnością użytkownikowi w tylnym stosie nawigacyjnym i lepiej unikać odrzuceniem działania szczegółów i pozostawienie tylko aktywności głównej:

Duży wyświetlacz z aktywnością na liście i szczegółami obok siebie.
          Nawigacja wstecz nie może odrzucić aktywności szczegółów i opuścić listy
          aktywność na ekranie.

Mały wyświetlacz tylko z aktywnością szczegółów. Nie można przejść wstecz
          odrzuć aktywność związaną z szczegółami i odkryj aktywność na liście.

Możesz natomiast zakończyć obie aktywności w tym samym czasie, korzystając z Atrybut 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 podzielonym kontenerze umożliwia użytkownikom dostęp treści. Na przykład w przypadku podziału na szczegóły listy użytkownik może chcieć przejść do sekcji i zachować sekcję ze szczegółami, ale nie zmieniać głównej funkcji:

Rysunek 18. Działanie otwarte w panelu dodatkowym w oknie zadania.

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 wtedy wrócić do poprzedniego poziomu szczegółów, wracając do poprzedniego poziomu w ramach stosu:

Rysunek 19. Aktywność została usunięta z góry stosu.

Nakładanie się działań na siebie jest domyślne. aktywności są uruchamiane z aktywności w tym samym kontenerze dodatkowym. Działania uruchomione z kontenera głównego w ramach aktywnego podziału również się kończą w kontenerze dodatkowym u góry stosu aktywności.

Działania w nowym zadaniu

Gdy działania w podzielonym oknie zadania rozpoczynają działania w nowym zadaniu, zadanie jest oddzielne od zadania, które obejmuje podział, i jest wyświetlane w całości okno. Ekran Ostatnie przedstawia dwa zadania: zadanie w podziale i nowe zadanie.

Rysunek 20. Rozpocznij zadanie C w nowym zadaniu na podstawie aktywności B.

Zastępowanie aktywności

Działania można zastępować w dodatkowym stosie kontenerów. na przykład gdy aktywność podstawowa służy do nawigacji najwyższego poziomu, a aktywność pomocnicza aktywność to wybrane miejsce docelowe. Każda opcja z listy najwyższego poziomu nawigacja powinna rozpocząć nową aktywność w kontenerze dodatkowym i usunąć wcześniejsze czynności.

Rysunek 21. Aktywność nawigacji najwyższego poziomu w panelu głównym zastępuje działania miejsca docelowego w panelu dodatkowym.

Jeśli aplikacja nie zakończy działania w kontenerze dodatkowym, gdy wybór nawigacji, przechodzenie wstecz może być mylące, gdy część podzielona jest zwinięta (po złożeniu urządzenia). Na przykład, jeśli masz w panelu głównym oraz na ekranach A i B nałożonych na stos w panelu dodatkowym. gdy użytkownik składa telefon, symbol B znajduje się na szczycie A, a A na górze 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ślne zachowanie przy uruchamianiu z boku w nowym kontenerze przez istniejący podział to umieszczenie nowych kontenerów dodatkowych na górze i zachowanie te z tyłu. Możesz skonfigurować podziały, aby usunąć poprzedni kontenery dodatkowe z clearTop i uruchamiać nowe działania w normalny sposób.

<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 aktywności dodatkowej i z poziomu głównego (menu) aktywność wysyła nowe intencje, które docierają do tej samej instancji, ale wywołują lub aktualizację interfejsu użytkownika w kontenerze.

Wiele podziałów

Aplikacje mogą zapewniać głęboką nawigację na wielu poziomach, uruchamiając dodatkowe z bliska.

Gdy działanie w kontenerze dodatkowym uruchamia nową aktywność z boku, nowy podział danych zostanie utworzony w stosunku do istniejącego.

Rysunek 22. Aktywność B rozpoczyna aktywność C z boku.

Stos tylny zawiera wszystkie wcześniej otwierane działania, więc użytkownicy może przejść do podziału A/B po skończeniu C.

Działania A, B i C w grupie. Ćwiczenia są łączone w grupy
          w następującej kolejności od góry do dołu: C, B, A.

Aby utworzyć nowy podział, uruchom nową aktywność z boku istniejącego kontener dodatkowy. Zadeklaruj konfiguracje dla kategorii A/B i B/C dzieli się i uruchamia 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 działania w aplikacji mogą mieć elementy interfejsu o identycznym działaniu funkcji; na przykład element sterujący, który otwiera okno z kontem ustawieniach.

Rysunek 23. Różne działania z funkcjonalnie identycznymi elementami interfejsu.

Jeśli 2 działania, które mają wspólny element interfejsu, są częścią przydziału, Zbędne i wprowadzające w błąd wyświetlanie elementu w obu działaniach.

Rysunek 24. Zduplikowane elementy interfejsu w przypadku podziału aktywności.

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 wtedy, gdy działanie jest zatrzymano. Słuchacze powinni zwykle być zarejestrowani w tych krajach: onStart() i wyrejestrowano w: onStop().

Widok modalny z pełnym oknem

Niektóre działania uniemożliwiają użytkownikom interakcję z aplikacją do czasu wykonywane jest określone działanie; np. aktywność na ekranie logowania, zasady ekranu z potwierdzeniem lub komunikatem o błędzie. Aktywności modalne powinny być zablokowane przed podziałem.

Działanie może wymusić zawsze wypełnienie okna zadania za pomocą polecenia rozwijania Konfiguracja:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

Kończenie działań

Użytkownicy mogą dokończyć aktywność po obu stronach podziału, przesuwając palcem od krawędzi wyświetlacza:

Rysunek 25. Kończenie ćwiczenia za pomocą gestu przesuwania B.
.
Rysunek 26. Gest przesuwania na końcu ćwiczenia A.

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 – czynności, którego dotknięto, uruchomiono 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 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>

Podział zawierający aktywności A i B. A skończyło się, a B –
          zajmuje całe okno.

Podział zawierający aktywności A i B. skończono B, a A do
          zajmuje całe okno.

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>

Podział zawierający aktywności A i B. skończyliśmy B, a także
          kończy działanie A, pozostawiając okno zadania puste.

Podział zawierający aktywności A i B. A skończyło, zostawiając B bez zmian
          w oknie zadania.

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>

Podział zawierający aktywności A i B. A to już koniec, a także
          kończy działanie B, pozostawiając okno zadania puste.

Podział zawierający aktywności A i B. skończono B, zostawiasz A bez zmian.
          w oknie zadania.

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>

Podział zawierający aktywności A i B. A to już koniec, a także
          kończy działanie B, pozostawiając okno zadania puste.

Podział zawierający aktywności A i B. skończyliśmy B, a także
          kończy działanie A, pozostawiając okno zadania puste.

Kończenie wielu działań w kontenerach

Jeśli wiele aktywności znajduje się w dzielonym kontenerze, zakończenie danej aktywności znajdujące się na dole stosu nie kończy automatycznie działań na górze.

Jeśli na przykład w kontenerze dodatkowym znajdują się 2 działania, nadrzędne są działania C nad nimi:

Dodatkowy stos aktywności zawierający aktywność C nakładany na grupę B
          jest nakładany na podstawowy stos aktywności zawierający aktywność
          Odpowiedź:

a konfiguracja podziału jest zdefiniowana przez konfigurację zadania A i B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

do zakończenia głównej aktywności zachowuje podział.

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          drugorzędny, C nałożone na B. C kończy, pozostawiając A i B w
          podział aktywności.

Zakończenie działania dolnego (głównego) kontenera dodatkowego nie powoduje usunąć wyświetlane na niej aktywności; więc zachowuje ten podział.

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          drugorzędny, C nałożone na B. B kończy, a A i C pozostają w
          podział aktywności.

Wszelkie dodatkowe reguły dotyczące kończenia ćwiczeń razem, np. zakończenie. jest też wykonywane działanie dodatkowe z aktywnością główną:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          kontener dodatkowy C umieszczony nad kontenerem B. Meta
          na końcu B i C.

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>

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          drugorzędny, C nałożone na B. C kończy, pozostawiając A i B w
          podział aktywności.

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          drugorzędny, C nałożone na B. B kończy, a A i C pozostają w
          podział aktywności.

Podziel z aktywnością A w kontenerze głównym oraz aktywności B i C w
          drugorzędny, C nałożone na B. A na finiszach, na końcu B
          C:

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 ma wpływ na dodatkowe uruchomienia aktywności i nowe kontenery, a nie istniejące i aktywne podziały.

Aby zmienić właściwości aktywnych podziałów, zakończ aktywność dodatkową lub i uruchomić ponownie z nową konfiguracją.

Wyodrębnij aktywność z podzielonego do pełnego okna

Utwórz nową konfigurację, która wyświetli pełne okno aktywności bocznej. a następnie ponownie uruchom aktywność z intencją prowadzą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; OEM) mogą wdrożyć umieszczanie aktywności jako funkcję systemu urządzeń. system określa reguły podziału dla aplikacji o wielu aktywnościach, zastępując ustawianie okien działanie aplikacji. Zastąpienie przez system wymusza na aplikacjach z wieloma aktywnościami zdefiniowany przez system tryb umieszczania aktywności.

Umieszczanie aktywności w systemie może uatrakcyjnić prezentację aplikacji przez wiele paneli układy, takie jak list-detail, bez żadnych zmian w aplikacji. Umieszczanie aktywności w systemie może jednak powodują również nieprawidłowe układy aplikacji, błędy lub konflikty z umieszczaniem aktywności implementowana przez aplikację.

Aplikacja może zablokować umieszczanie aktywności systemu lub zezwolić na to, ustawiając parametr property w pliku manifestu aplikacji np.:

<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 programie Jetpack WindowManager. WindowWłaściwości. Ustaw wartość false, jeśli w Twojej aplikacji jest zaimplementowane umieszczanie aktywności lub chcesz w inny sposób uniemożliwić systemowi stosowanie reguł umieszczania aktywności do Twojej aplikacji. Ustaw wartość na true, aby system mógł go zastosować. zdefiniowane przez system aktywność w aplikacji.

Ograniczenia, ograniczenia i zastrzeżenia

  • Tylko hostująca aplikacja zadania, która jest identyfikowana jako właściciel katalogu głównego może porządkować 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 do innej aplikacji, osadzanie i podziały nie będą działać w przypadku tych działań.
  • Czynności można uporządkować tylko w ramach jednego zadania. Uruchamianie działania w nowym zadaniu zawsze umieszcza je w nowym, rozszerzonym oknie poza żadnym istniejące podziały.
  • Tylko działania w ramach tego samego procesu mogą być porządkowane i podzielone na segmenty. SplitInfo wywołanie zwrotne raportuje tylko działania należące 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ń aktywności, które następuje po zarejestrowaniu reguły. Obecnie nie ma możliwości aktualizować istniejących podziałów lub ich właściwości wizualnych.
  • Konfiguracja filtra pary podzielonej musi pasować do intencji używanych, gdy całkowicie rozpocząć działania. Dopasowanie następuje w momencie, gdy nowy jest rozpoczynana w trakcie procesu zgłaszania, więc może nie wiedzieć o nazwy komponentów, które są rozpoznawane w dalszej części procesu systemowego przy użyciu funkcji niejawne intencje. Jeśli w momencie uruchomienia nazwa komponentu nie jest znana, zamiast niego można użyć symbolu wieloznacznego („*/*”), a filtrowanie na działanie intencji.
  • Obecnie nie można przenosić działań między kontenerami ani do nich i z powrotem podziałów po ich utworzeniu. Podziały są tworzone tylko przez po uruchomieniu nowych działań z pasującymi regułami w bibliotece WindowManager, i podziały zostają zniszczone, gdy ostatnia aktywność w podzielonym kontenerze .
  • Działania można uruchamiać ponownie po zmianie konfiguracji, np. w przypadku zostanie utworzony lub usunięty, a granice aktywności zmienią się, aktywność może przez całkowite zniszczenie poprzedniej instancji i utworzenie nowy. Dlatego deweloperzy aplikacji powinni uważać na: 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

. .