Einbetten von Aktivitäten

Durch das Einbetten von Aktivitäten werden Apps auf Geräten mit großen Bildschirmen optimiert. Dabei wird das Aufgabenfenster einer App auf zwei Aktivitäten oder zwei Instanzen derselben Aktivität aufgeteilt.

Abbildung 1: Einstellungen-App mit Aktivitäten nebeneinander.

Wenn deine App mehrere Aktivitäten umfasst, kannst du mit dem Einbetten von Aktivitäten die Nutzerfreundlichkeit auf Tablets, faltbaren Geräten und ChromeOS-Geräten verbessern.

Für das Einbetten von Aktivitäten ist keine Code-Refaktorierung erforderlich. Sie legen fest, wie Ihre App ihre Aktivitäten nebeneinander oder gestapelt anzeigt, indem Sie eine XML-Konfigurationsdatei erstellen oder Jetpack WindowManager-API-Aufrufe ausführen.

Kleine Bildschirme werden automatisch unterstützt. Wenn Ihre App auf einem Gerät mit einem kleinen Bildschirm ausgeführt wird, werden Aktivitäten übereinander gestapelt. Auf großen Bildschirmen werden Aktivitäten nebeneinander angezeigt. Das System bestimmt die Präsentation anhand der von Ihnen erstellten Konfiguration. Eine Verzweigungslogik ist nicht erforderlich.

Die Einbettung von Aktivitäten berücksichtigt Änderungen der Geräteausrichtung und funktioniert reibungslos auf faltbaren Geräten. Beim Auf- und Zuklappen werden Aktivitäten gestapelt bzw. entstapelt.

Das Einbetten von Aktivitäten wird auf den meisten Geräten mit großen Bildschirmen mit Android 12L (API-Level 32) und höher unterstützt.

Aufgabenfenster teilen

Bei der Aktivitätseinbettung wird das Fenster der Anwendungsaufgabe in zwei Container aufgeteilt: in einen primären und einen sekundären Container. Die Container enthalten Aktivitäten, die von der Hauptaktivität oder von anderen Aktivitäten, die sich bereits in den Containern befinden, gestartet wurden.

Aktivitäten werden beim Starten im sekundären Container gestapelt. Bei kleinen Bildschirmen wird der sekundäre Container über dem primären Container gestapelt. Das Stapeln von Aktivitäten und die Rückwärtsnavigation entsprechen daher der Reihenfolge der Aktivitäten, die bereits in Ihre App integriert sind.

Durch das Einbetten von Aktivitäten können Sie Aktivitäten auf verschiedene Arten anzeigen. Ihre App kann das Aufgabenfenster teilen, indem sie zwei Aktivitäten gleichzeitig nebeneinander startet:

Abbildung 2. Zwei Aktivitäten nebeneinander.

Oder eine Aktivität, die das gesamte Aufgabenfenster einnimmt, kann eine Aufteilung erstellen, indem eine neue Aktivität zusammen mit folgenden gestartet wird:

Abbildung 3: Aktivität A startet Aktivität B an der Seite.

Aktivitäten, die bereits aufgeteilt sind und ein Aufgabenfenster teilen, können auf folgende Arten andere Aktivitäten starten:

  • Seitlich über einer anderen Aktivität:

    Abbildung 4: Aktivität A startet Aktivität C neben Aktivität B.
  • Verschieben Sie die Teilung zur Seite und verschieben Sie sie zur Seite, um die vorherige primäre Aktivität zu verbergen:

    Abbildung 5: Aktivität B startet Aktivität C zur Seite und verschiebt den Split zur Seite.
  • Starten Sie eine Aktivität oben, d. h. im selben Aktivitätsstapel:

    Abbildung 6: Aktivität B startet Aktivität C ohne zusätzliche Intent-Flags.
  • Öffnen Sie in derselben Aufgabe ein vollständiges Aktivitätsfenster:

    Abbildung 7: Aktivität A oder Aktivität B startet Aktivität C, die das Aufgabenfenster ausfüllt.

Rückwärtsnavigation

Verschiedene Arten von Anwendungen können in einem Fensterstatus für aufgeteilte Aufgaben unterschiedliche Regeln für die Zurücknavigation haben. Dies hängt von den Abhängigkeiten zwischen den Aktivitäten oder davon ab, wie Nutzer das Back-Ereignis auslösen. Beispiele:

  • Zusammenführen: Wenn Aktivitäten miteinander in Zusammenhang stehen und eine Aktivität nicht ohne die andere angezeigt werden soll, kann die Zurück-Navigation so konfiguriert werden, dass beide beendet werden.
  • Alleine: Wenn Aktivitäten völlig unabhängig sind, hat die Rückwärtsnavigation bei einer Aktivität keinen Einfluss auf den Status einer anderen Aktivität im Aufgabenfenster.

Das Zurück-Ereignis wird bei Verwendung der Schaltflächennavigation an die letzte fokussierte Aktivität gesendet. Bei der gestenbasierten Navigation wird das Zurück-Ereignis an die Aktivität gesendet, bei der die Geste aufgetreten ist.

Mehrfensterlayout

Jetpack WindowManager ermöglicht es Ihnen, ein Mehrfensterlayout mit Aktivitäten zu erstellen, das auf Geräten mit großen Bildschirmen mit Android 12L (API-Level 32) oder höher und auf einigen Geräten mit älteren Plattformversionen funktioniert. Vorhandene Apps, die auf mehreren Aktivitäten statt auf Fragmenten oder ansichtsbasierten Layouts wie SlidingPaneLayout basieren, bieten eine bessere Nutzererfahrung auf großen Bildschirmen, ohne den Quellcode refaktorieren zu müssen.

Ein gängiges Beispiel ist die Aufteilung zwischen Listen und Details. Um eine hochwertige Darstellung zu gewährleisten, startet das System die Listenaktivität. Anschließend startet die Anwendung sofort die Detailaktivität. Das Übergangssystem wartet, bis beide Aktivitäten gezeichnet sind, und zeigt sie dann zusammen an. Für die Nutzenden beginnen die beiden Aktivitäten als eine.

Abbildung 8. Zwei Aktivitäten wurden gleichzeitig in einem Mehrfensterlayout gestartet.

Aufteilungsattribute

Sie können angeben, wie das Aufgabenfenster zwischen den aufgeteilten Containern und wie die Container relativ zueinander angeordnet werden sollen.

Legen Sie für Regeln, die in einer XML-Konfigurationsdatei definiert sind, die folgenden Attribute fest:

  • splitRatio: Legt die Containerproportionen fest. Der Wert ist eine Gleitkommazahl im offenen Intervall (0,0, 1,0).
  • splitLayoutDirection: gibt an, wie die geteilten Container relativ zueinander angeordnet sind. Mögliche Werte:
    • ltr: von links nach rechts
    • rtl: Von rechts nach links
    • locale: Entweder ltr oder rtl wird anhand der Spracheinstellung ermittelt

Beispiele finden Sie unten im Abschnitt XML-Konfiguration.

Erstellen Sie für Regeln, die mit den WindowManager-APIs erstellt wurden, ein SplitAttributes-Objekt mit SplitAttributes.Builder und rufen Sie die folgenden Builder-Methoden auf:

Beispiele finden Sie unten im Abschnitt WindowManager API.

Abbildung 9. Zwei Aktivitätsaufteilungen sind von links nach rechts angeordnet, aber mit unterschiedlichen Aufteilungsverhältnissen.

Platzhalter

Platzhalteraktivitäten sind leere sekundäre Aktivitäten, die einen Bereich einer Aktivitätsaufteilung einnehmen. Sie sollen letztendlich durch eine andere Aktivität ersetzt werden, die Inhalte enthält. Eine Platzhalteraktivität kann beispielsweise die sekundäre Seite einer Aktivitätsaufteilung in einem Listendetail-Layout belegen, bis ein Element aus der Liste ausgewählt wird. Dann ersetzt eine Aktivität, die die Detailinformationen für das ausgewählte Listenelement enthält, den Platzhalter.

Standardmäßig zeigt das System Platzhalter nur dann an, wenn genügend Platz für eine Aktivitätsaufteilung vorhanden ist. Platzhalter werden automatisch fertig gestellt, wenn die Breite oder Höhe zu klein für eine Aufteilung ist. Wenn der Platz ausreicht, startet das System den Platzhalter mit einem neu initialisierten Zustand neu.

Abbildung 10. Faltbares Gerät auf- und zuklappen Die Platzhalteraktivität ist abgeschlossen und wird neu erstellt, wenn sich die Anzeigegröße ändert.

Das Attribut stickyPlaceholder einer SplitPlaceholderRule- oder setSticky()-Methode von SplitPlaceholder.Builder kann jedoch das Standardverhalten überschreiben. Wenn das Attribut oder die Methode den Wert true angibt, zeigt das System den Platzhalter als oberste Aktivität im Aufgabenfenster an, wenn die Anzeige von einer zweiteiligen Anzeige auf eine einzelne Ansicht verkleinert wird (siehe Beispiel unter Split-Konfiguration).

Abbildung 11. Faltbares Gerät auf- und zuklappen Die Platzhalteraktivität ist fixiert.

Änderungen der Fenstergröße

Wenn Änderungen an der Gerätekonfiguration die Breite des Aufgabenfensters reduzieren, damit es nicht groß genug für ein Layout mit mehreren Bereichen ist (z. B. wenn ein faltbares Gerät mit einem großen Bildschirm von der Tabletgröße auf das Smartphone geklappt wird oder die Größe des App-Fensters im Mehrfenstermodus angepasst wird), werden die Aktivitäten, die keine Platzhalter sind, im sekundären Bereich des Aufgabenfensters über den Aktivitäten im Hauptbereich gestapelt.

Platzhalteraktivitäten werden nur angezeigt, wenn die Anzeigebreite für eine Aufteilung ausreicht. Auf kleineren Bildschirmen wird der Platzhalter automatisch geschlossen. Wird der Anzeigebereich wieder groß genug, wird der Platzhalter neu erstellt. Siehe Platzhalter oben.

Das Stapeln von Aktivitäten ist möglich, da WindowManager die Aktivitäten im sekundären Bereich über den Aktivitäten im primären Bereich geordnet.

Mehrere Aktivitäten im sekundären Bereich

Aktivität B startet Aktivität C ohne zusätzliche Intent-Flags:

Aktivitätsaufteilung, die die Aktivitäten A, B und C enthält, wobei C über B gestapelt ist.

zur folgenden Z-Reihenfolge von Aktivitäten in derselben Aufgabe führt:

Sekundärer Aktivitätsstapel mit Aktivität C, gestapelt über B.
          Der sekundäre Stack wird über den allgemeinen Aktivitätsstapel gestapelt, der Aktivität A enthält.

In einem kleineren Aufgabenfenster wird die Anwendung also auf eine einzelne Aktivität verkleinert, wobei sich C oben im Stack befindet:

Kleines Fenster, in dem nur Aktivität C angezeigt wird.

Wenn Sie im kleineren Fenster zurückgehen, werden die übereinander gestapelten Aktivitäten angezeigt.

Wenn die Konfiguration des Aufgabenfensters für mehrere Bereiche wiederhergestellt wird, werden die Aktivitäten wieder nebeneinander angezeigt.

Gestapelte Splits

Aktivität B startet Aktivität C zur Seite und verschiebt die Aufteilung zur Seite:

Aufgabenfenster mit den Aktivitäten A und B und dann den Aktivitäten B und C.

Das Ergebnis ist die folgende Z-Reihenfolge von Aktivitäten in derselben Aufgabe:

Aktivitäten A, B und C in einem Stapel. Die Aktivitäten sind in der folgenden Reihenfolge von oben nach unten gestapelt: C, B, A.

In einem kleineren Aufgabenfenster verkleinert sich die Anwendung auf eine einzelne Aktivität mit C:

Kleines Fenster, in dem nur Aktivität C angezeigt wird.

Hochformat fest

Mit der Manifesteinstellung android:screenOrientation können Apps Aktivitäten auf das Hoch- oder Querformat beschränken. Um die Nutzerfreundlichkeit auf Geräten mit großen Bildschirmen wie Tablets und faltbaren Geräten zu verbessern, können Gerätehersteller (OEMs) Anfragen zur Bildschirmausrichtung ignorieren und die App auf Displays im Querformat im Hochformat und auf Displays im Querformat im Hochformat anzeigen lassen.

Abbildung 12. Aktivitäten im Letterbox-Format: festes Hochformat auf dem Gerät im Querformat (links), festes Querformat auf dem Gerät im Hochformat (rechts).

Wenn das Einbetten von Aktivitäten aktiviert ist, können OEMs die Geräte auch an Aktivitäten im Letterbox-Format im Querformat auf großen Bildschirmen (Breite ≥ 600 dp) anpassen. Wenn eine Aktivität im festen Hochformat eine zweite Aktivität startet, kann das Gerät die beiden Aktivitäten nebeneinander in einem Bildschirm mit zwei Bereichen anzeigen.

Abbildung 13. Bei Aktivität A im Hochformat wird Aktivität B seitlich gestartet.

Füge deiner App-Manifestdatei immer die Eigenschaft android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED hinzu, um Geräte darüber zu informieren, dass deine App das Einbetten von Aktivitäten unterstützt (siehe Split-Konfiguration unten). OEM-spezifische Geräte können dann ermitteln, ob Aktivitäten im Letterbox-Format mit festem Hochformat ausgeführt werden sollen.

Split-Konfiguration

Mit Aufteilungsregeln werden Aktivitätsaufteilungen konfiguriert. Aufteilungsregeln werden in einer XML-Konfigurationsdatei oder durch WindowManager-API-Aufrufe in Jetpack definiert.

In beiden Fällen muss Ihre App auf die WindowManager-Bibliothek zugreifen und das System darüber informieren, dass die App das Einbetten von Aktivitäten implementiert hat.

Unternimm Folgendes:

  1. Fügen Sie der Datei build.gradle Ihrer App auf Modulebene die neueste Abhängigkeit von der WindowManager-Bibliothek hinzu. Beispiel:

    implementation 'androidx.window:window:1.1.0-beta02'

    Die WindowManager-Bibliothek bietet alle Komponenten, die für das Einbetten von Aktivitäten erforderlich sind.

  2. Informieren Sie das System darüber, dass in Ihrer App die Einbettung von Aktivitäten implementiert ist.

    Fügen Sie das Attribut android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED dem Element <application> der App-Manifestdatei hinzu und setzen Sie den Wert auf „true“. Beispiel:

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

    In WindowManager Version 1.1.0-alpha06 und höher sind Aufteilungen zum Einbetten von Aktivitäten deaktiviert, es sei denn, die Eigenschaft wird dem Manifest hinzugefügt und auf "true" gesetzt.

    Außerdem verwenden Gerätehersteller die Einstellung, um benutzerdefinierte Funktionen für Apps zu aktivieren, die das Einbetten von Aktivitäten unterstützen. Beispielsweise können Geräte eine Aktivität im Hochformat auf Displays im Querformat mit Letterbox-Bild versehen, um die Aktivität für den Wechsel zu einem Zweifenster-Layout zu orientieren, wenn eine zweite Aktivität beginnt (siehe Feste Hochformat-Ausrichtung).

XML-Konfiguration

Führen Sie die folgenden Schritte aus, um eine XML-basierte Implementierung der Aktivitätseinbettung zu erstellen:

  1. Erstellen Sie eine XML-Ressourcendatei mit folgenden Aufgaben:

    • Definiert Aktivitäten, die eine Aufteilung teilen
    • Konfiguriert die Aufteilungsoptionen
    • Erstellt einen Platzhalter für den sekundären Container der Aufteilung, wenn kein Inhalt verfügbar ist
    • Gibt Aktivitäten an, die nie Teil einer Aufteilung sein sollten

    Beispiele:

    <!-- 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. Erstellen Sie einen Initialisierer.

    Die WindowManager-Komponente RuleController parst die XML-Konfigurationsdatei und stellt die Regeln für das System zur Verfügung. Eine Startup-Bibliothek Initializer von Jetpack stellt RuleController beim Start der Anwendung die XML-Datei zur Verfügung, damit die Regeln beim Start von Aktivitäten wirksam werden.

    So erstellen Sie einen Initialisierer:

    1. Fügen Sie der Datei build.gradle auf Modulebene die neueste Abhängigkeit der Jetpack-Startbibliothek hinzu. Beispiel:

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

    2. Erstellen Sie eine Klasse, die die Initializer-Schnittstelle implementiert.

      Der Initialisierer macht die Aufteilungsregeln für RuleController verfügbar, indem er die ID der XML-Konfigurationsdatei (main_split_config.xml) an die Methode RuleController.parseRules() übergibt.

      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. Erstellen Sie einen Contentanbieter für die Regeldefinitionen.

    Füge deiner App-Manifestdatei androidx.startup.InitializationProvider als <provider> hinzu. Fügen Sie einen Verweis auf die Implementierung des RuleController-Initialisierers SplitInitializer ein:

    <!-- 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 erkennt und initialisiert SplitInitializer, bevor die Methode onCreate() der App aufgerufen wird. Folglich gelten die Aufteilungsregeln zu dem Zeitpunkt, zu dem die Hauptaktivität der App beginnt.

WindowManager-API

Mit einigen wenigen API-Aufrufen lässt sich die Einbettung von Aktivitäten programmatisch implementieren. Führen Sie die Aufrufe in der Methode onCreate() einer Unterklasse von Application aus, um sicherzustellen, dass die Regeln vor dem Start von Aktivitäten wirksam sind.

So erstellen Sie eine Aktivitätsaufteilung programmatisch:

  1. Erstellen Sie eine Aufteilungsregel:

    1. Erstellen Sie einen SplitPairFilter, der die Aktivitäten identifiziert, die die Aufteilung teilen:

      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. Fügen Sie den Filter einer Filtergruppe hinzu:

      Kotlin

      val filterSet = setOf(splitPairFilter)
      

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
      
    3. Erstellen Sie Layoutattribute für die Aufteilung:

      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 erstellt ein Objekt mit Layoutattributen:

      • setSplitType: Definiert, wie der verfügbare Anzeigebereich jedem Aktivitätscontainer zugewiesen wird. Der Verhältnisaufteilungstyp gibt den Anteil des verfügbaren Anzeigebereichs an, der dem primären Container zugewiesen ist. Der sekundäre Container nimmt den Rest des verfügbaren Anzeigebereichs ein.
      • setLayoutDirection: Gibt an, wie die Aktivitätscontainer im Verhältnis zueinander angeordnet werden, wobei der primäre Container zuerst angezeigt wird.
    4. Erstellen Sie ein 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();
      

      Mit SplitPairRule.Builder wird die Regel erstellt und konfiguriert:

      • filterSet: Enthält Filter für aufgeteilte Paare, die bestimmen, wann die Regel angewendet werden soll. Dazu werden Aktivitäten identifiziert, die eine Aufteilung teilen.
      • setDefaultSplitAttributes: Wendet Layoutattribute auf die Regel an.
      • setMinWidthDp: Legt die minimale Anzeigebreite (in dichteunabhängige Pixeln, dp) fest, die eine Aufteilung ermöglicht.
      • setMinSmallestWidthDp: Legt den Mindestwert (in dp) fest, um den die kleinere der beiden Bildschirmabmessungen erforderlich ist, um unabhängig von der Ausrichtung des Geräts eine Aufteilung zu ermöglichen.
      • setMaxAspectRatioInPortrait: Legt das maximale Seitenverhältnis (Höhe:Breite) im Hochformat fest, für das Aktivitätsaufteilungen angezeigt werden. Wenn das Seitenverhältnis eines Bildschirms im Hochformat das maximale Seitenverhältnis überschreitet, werden Teilungen unabhängig von der Breite des Bildschirms deaktiviert. Hinweis:Der Standardwert ist 1, 4.Auf den meisten Tablets nehmen Aktivitäten das gesamte Aufgabenfenster im Hochformat ein. Weitere Informationen finden Sie unter SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT und setMaxAspectRatioInLandscape. Der Standardwert für das Querformat ist ALWAYS_ALLOW.
      • setFinishPrimaryWithSecondary: Damit wird festgelegt, wie sich das Abschließen aller Aktivitäten im sekundären Container auf die Aktivitäten im primären Container auswirkt. NEVER gibt an, dass das System die primären Aktivitäten nicht beenden soll, wenn alle Aktivitäten im sekundären Container beendet sind (siehe Aktivitäten beenden).
      • setFinishSecondaryWithPrimary: Damit wird festgelegt, wie sich das Abschließen aller Aktivitäten im primären Container auf die Aktivitäten im sekundären Container auswirkt. ALWAYS gibt an, dass das System die Aktivitäten im sekundären Container immer beenden soll, wenn alle Aktivitäten im primären Container abgeschlossen sind (siehe Aktivitäten beenden).
      • setClearTop: Gibt an, ob alle Aktivitäten im sekundären Container beendet sind, wenn eine neue Aktivität im Container gestartet wird. Falsch bedeutet, dass neue Aktivitäten über Aktivitäten gestapelt werden, die sich bereits im sekundären Container befinden.
    5. Rufen Sie die Singleton-Instanz von WindowManager RuleController ab und fügen Sie die folgende Regel hinzu:

      Kotlin

      val ruleController = RuleController.getInstance(this)
      ruleController.addRule(splitPairRule)
      

      Java

      RuleController ruleController = RuleController.getInstance(this);
      ruleController.addRule(splitPairRule);
      
  2. Erstellen Sie einen Platzhalter für den sekundären Container, wenn kein Inhalt verfügbar ist:

    1. Erstellen Sie ein ActivityFilter, das die Aktivität identifiziert, mit der der Platzhalter ein Aufgabenfenster teilt:

      Kotlin

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )
      

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
      
    2. Fügen Sie den Filter einer Filtergruppe hinzu:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
      

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
      
    3. Erstellen Sie ein 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();
      

      Mit SplitPlaceholderRule.Builder wird die Regel erstellt und konfiguriert:

      • placeholderActivityFilterSet: Enthält Aktivitätsfilter, die bestimmen, wann die Regel angewendet wird, indem Aktivitäten identifiziert werden, mit denen die Platzhalteraktivität verknüpft ist.
      • Intent: Gibt den Start der Platzhalteraktivität an.
      • setDefaultSplitAttributes: Wendet Layoutattribute auf die Regel an.
      • setMinWidthDp: Legt die minimale Anzeigebreite (in dichteunabhängigen Pixeln, dp) fest, die eine Aufteilung zulässt.
      • setMinSmallestWidthDp: Legt den Mindestwert (in dp) fest, um den die kleinere der beiden Bildschirmabmessungen erforderlich ist, um unabhängig von der Geräteausrichtung eine Aufteilung zu ermöglichen.
      • setMaxAspectRatioInPortrait: Legt das maximale Seitenverhältnis (Höhe:Breite) im Hochformat fest, für das Aktivitätsaufteilungen angezeigt werden. Hinweis: Der Standardwert ist 1,4.Dies führt auf den meisten Tablets dazu, dass das Aufgabenfenster im Hochformat ausgefüllt wird. Weitere Informationen finden Sie unter SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT und setMaxAspectRatioInLandscape. Der Standardwert für das Querformat ist ALWAYS_ALLOW.
      • setFinishPrimaryWithPlaceholder: Damit wird festgelegt, wie sich das Abschließen der Platzhalteraktivität auf die Aktivitäten im primären Container auswirkt. ALWAYS gibt an, dass das System die Aktivitäten im primären Container immer beenden soll, wenn der Platzhalter abgeschlossen ist (siehe Aktivitäten beenden).
      • setSticky: Bestimmt, ob die Platzhalteraktivität auf kleinen Displays über dem Aktivitätsstapel angezeigt wird, sobald der Platzhalter zum ersten Mal in einem Split mit ausreichender Mindestbreite erschienen ist.
    4. Fügen Sie die Regel zu WindowManager RuleController hinzu:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)
      

      Java

      ruleController.addRule(splitPlaceholderRule);
      
  3. Geben Sie Aktivitäten an, die nie Teil einer Aufteilung sein sollen:

    1. Erstellen Sie ein ActivityFilter, das eine Aktivität kennzeichnet, die immer den gesamten Anzeigebereich der Aufgabe einnehmen soll:

      Kotlin

      val expandedActivityFilter = ActivityFilter(
        ComponentName(this, ExpandedActivity::class.java),
        null
      )
      

      Java

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
      
    2. Fügen Sie den Filter einer Filtergruppe hinzu:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)
      

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
      
    3. Erstellen Sie ein ActivityRule:

      Kotlin

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()
      

      Java

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();
      

      Mit ActivityRule.Builder wird die Regel erstellt und konfiguriert:

      • expandedActivityFilterSet: Enthält Aktivitätsfilter, die bestimmen, wann die Regel angewendet wird, indem Aktivitäten identifiziert werden, die Sie aus Aufteilungen ausschließen möchten.
      • setAlwaysExpand: Gibt an, ob die Aktivität das gesamte Aufgabenfenster ausfüllen soll.
    4. Fügen Sie die Regel zu WindowManager RuleController hinzu:

      Kotlin

      ruleController.addRule(activityRule)
      

      Java

      ruleController.addRule(activityRule);
      

Anwendungsübergreifende Einbettung

Unter Android 13 (API-Level 33) und höher können Apps Aktivitäten aus anderen Apps einbetten. Die anwendungsübergreifende Einbettung von Aktivitäten bzw. UID-übergreifende Einbettungen ermöglicht die visuelle Integration von Aktivitäten aus mehreren Android-Apps. Wie bei der Einbettung von Aktivitäten in einer App zeigt das System eine Aktivität der Host-App und einer eingebetteten Aktivität einer anderen App nebeneinander oder oben und unten auf dem Bildschirm an.

Die App „Einstellungen“ könnte beispielsweise die Aktivität zur Hintergrundauswahl aus der App „HintergründePicker“ einbetten:

Abbildung 14. App „Einstellungen“ (Menü links) mit Hintergrundauswahl als eingebettete Aktivität (rechts)

Vertrauensmodell

Hostprozesse, die Aktivitäten aus anderen Apps einbetten, können die Darstellung der eingebetteten Aktivitäten neu definieren, einschließlich Größe, Position, Zuschneiden und Transparenz. Böswillige Hosts können diese Funktion nutzen, um Nutzer in die Irre zu führen und Clickjacking oder andere UI-Redressing-Angriffe auszuführen.

Um den Missbrauch der Einbettung von App-übergreifenden Aktivitäten zu verhindern, müssen Apps bei Android das Einbetten ihrer Aktivitäten zulassen. Hosts können in Apps als vertrauenswürdig oder nicht vertrauenswürdig eingestuft werden.

Vertrauenswürdige Hosts

Damit andere Anwendungen die Darstellung von Aktivitäten in Ihrer App einbetten und vollständig steuern können, geben Sie das SHA-256-Zertifikat der Host-App im Attribut android:knownActivityEmbeddingCerts der Elemente <activity> oder <application> der Manifestdatei Ihrer App an.

Legen Sie den Wert von android:knownActivityEmbeddingCerts entweder als String fest:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

oder ein Array mit Strings, um mehrere Zertifikate anzugeben:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

die auf eine Ressource wie die folgende verweist:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

App-Inhaber können einen SHA-Zertifikat-Digest erhalten, indem sie die Gradle-Aufgabe signingReport ausführen. Der Zertifikat-Digest ist der SHA-256-Fingerabdruck ohne die trennenden Doppelpunkte. Weitere Informationen finden Sie unter Signaturbericht ausführen und Client authentifizieren.

Nicht vertrauenswürdige Hosts

Damit jede App die Aktivitäten Ihrer App einbetten und ihre Präsentation steuern kann, geben Sie das Attribut android:allowUntrustedActivityEmbedding im Element <activity> oder <application> im App-Manifest an. Beispiel:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

Der Standardwert des Attributs ist „false“, wodurch das Einbetten von App-übergreifenden Aktivitäten verhindert wird.

Benutzerdefinierte Authentifizierung

Erstellen Sie einen benutzerdefinierten Authentifizierungsmechanismus, der die Hostidentität bestätigt, um das Risiko der Einbettung nicht vertrauenswürdiger Aktivitäten zu verringern. Wenn Sie die Hostzertifikate kennen, verwenden Sie die androidx.security.app.authenticator-Bibliothek zur Authentifizierung. Wenn sich der Host nach dem Einbetten deiner Aktivität authentifiziert, kannst du den tatsächlichen Inhalt anzeigen. Falls nicht, kannst du den Nutzer darüber informieren, dass die Aktion nicht erlaubt war, und den Inhalt blockieren.

Verwenden Sie die Methode ActivityEmbeddingController#isActivityEmbedded() aus der Jetpack WindowManager-Bibliothek, um zu prüfen, ob ein Host Ihre Aktivität einbettet. Beispiel:

Kotlin

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

Java

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity);
}

Größenbeschränkung

Das Android-System wendet die im App-Manifest-Element <layout> angegebene Mindesthöhe und Breite auf eingebettete Aktivitäten an. Wenn für eine App keine Mindesthöhe und -breite angegeben wird, gelten die Systemstandardwerte (sw220dp).

Wenn der Host versucht, die Größe des eingebetteten Containers auf eine kleinere als die Mindestgröße anzupassen, wird der eingebettete Container erweitert, sodass er die gesamten Aufgabengrenzen einnimmt.

<Aktivitätsalias>

Damit die Einbettung von vertrauenswürdigen oder nicht vertrauenswürdigen Aktivitäten mit dem Element <activity-alias> funktioniert, muss android:knownActivityEmbeddingCerts oder android:allowUntrustedActivityEmbedding auf die Zielaktivität und nicht auf den Alias angewendet werden. Die Richtlinie, die die Sicherheit auf dem Systemserver überprüft, basiert auf den für das Ziel festgelegten Flags und nicht auf dem Alias.

Hostanwendung

Host-Anwendungen implementieren die Einbettung von App-übergreifenden Aktivitäten genauso wie das Einbetten von Einzel-App-Aktivitäten. Die Objekte SplitPairRule und SplitPairFilter oder ActivityRule und ActivityFilter geben eingebettete Aktivitäten und Aufteilungen von Aufgabenfenstern an. Aufteilungsregeln werden statisch in XML oder zur Laufzeit über Jetpack WindowManager API-Aufrufe definiert.

Wenn eine Host-Anwendung versucht, eine Aktivität einzubetten, für die die appübergreifende Einbettung nicht aktiviert wurde, belegt die Aktivität die gesamten Aufgabengrenzen. Daher müssen Host-Anwendungen wissen, ob die Zielaktivitäten App-übergreifende Einbettungen ermöglichen.

Wenn eine eingebettete Aktivität eine neue Aktivität in derselben Aufgabe startet und die neue Aktivität nicht für die App-übergreifende Einbettung aktiviert ist, belegt die Aktivität die gesamten Aufgabengrenzen, anstatt die Aktivität im eingebetteten Container zu überlagern.

Eine Host-Anwendung kann ihre eigenen Aktivitäten ohne Einschränkung einbetten, solange die Aktivitäten in derselben Aufgabe gestartet werden.

Beispiele für die Aufteilung

Vom ganzen Fenster trennen

Abbildung 15. Aktivität A startet Aktivität B an der Seite.

Es ist keine Refaktorierung erforderlich. Sie können die Konfiguration für die Aufteilung statisch oder während der Laufzeit definieren und dann Context#startActivity() ohne zusätzliche Parameter aufrufen.

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

Standardmäßig teilen

Wenn die Landingpage einer Anwendung so konzipiert ist, dass sie auf großen Bildschirmen in zwei Container aufgeteilt werden soll, ist die Nutzererfahrung am besten, wenn beide Aktivitäten gleichzeitig erstellt und präsentiert werden. Inhalte sind jedoch möglicherweise erst dann für den sekundären Container des Splits verfügbar, wenn der Nutzer mit der Aktivität im primären Container interagiert (z. B. ein Element aus einem Navigationsmenü auswählt). Eine Platzhalteraktivität kann die Lücke füllen, bis Inhalte im sekundären Container der Aufteilung angezeigt werden können (siehe Platzhalter oben).

Abbildung 16. Aufteilung durch gleichzeitiges Öffnen von zwei Aktivitäten erstellt. Eine Aktivität ist ein Platzhalter.

Um eine Aufteilung mit einem Platzhalter zu erstellen, erstellen Sie einen Platzhalter und verknüpfen Sie ihn mit der primären Aktivität:

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

Wenn eine Anwendung einen Intent empfängt, kann die Zielaktivität als sekundärer Teil einer Aktivitätsaufteilung angezeigt werden, z. B. eine Anfrage zum Anzeigen eines Detailbildschirms mit Informationen zu einem Element aus einer Liste. Auf kleinen Displays werden die Details im vollständigen Aufgabenfenster angezeigt, auf größeren Geräten neben der Liste.

Abbildung 17. Deeplink-Detailaktivität wird allein auf einem kleinen Bildschirm, aber zusammen mit einer Listenaktivität auf einem großen Bildschirm angezeigt.

Die Startanfrage sollte an die Hauptaktivität weitergeleitet werden und die Ziel-Detailaktivität sollte in einem Split gestartet werden. Das System wählt automatisch die richtige Präsentation – gestapelt oder nebeneinander – basierend auf der verfügbaren Bildschirmbreite aus.

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));
}

Das Deeplink-Ziel ist möglicherweise die einzige Aktivität, die dem Nutzer im Back-Navigations-Stack zur Verfügung stehen sollte. In diesem Fall sollten Sie die Detailaktivität nicht verwerfen, sondern nur die Hauptaktivität verlassen:

Großes Display mit einer Liste der Aktivitäten und der Aktivitätsdetails.
          Bei der Zurück-Navigation konnten keine Detailaktivitäten geschlossen und Listenaktivitäten nicht auf dem Bildschirm angezeigt werden.

Kleines Display nur mit Detailaktivität. Detailaktivitäten konnten nicht geschlossen und Listenaktivitäten nicht angezeigt werden.

Stattdessen kannst du beide Aktivitäten gleichzeitig mithilfe des Attributs finishPrimaryWithSecondary beenden:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

Siehe Konfigurationsattribute unten.

Mehrere Aktivitäten in aufgeteilten Containern

Durch das Stapeln mehrerer Aktivitäten in einem geteilten Container können Nutzer auf ausführliche Inhalte zugreifen. Bei einer Aufteilung zwischen Listen und Details kann es vorkommen, dass der Nutzer einen untergeordneten Detailabschnitt aufrufen muss, aber die primäre Aktivität beibehalten:

Abbildung 18. Aktivität wurde im sekundären Bereich des Aufgabenfensters geöffnet.

Kotlin

class DetailActivity {
    . . .
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

Java

public class DetailActivity {
    . . .
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

Die untergeordnete Detail-Aktivität wird über der Detailaktivität platziert und verbirgt sie:

Der Nutzer kann dann zur vorherigen Detailebene zurückkehren, indem er den Stapel zurücklegt:

Abbildung 19. Aktivität wurde oben im Stapel entfernt.

Aktivitäten werden standardmäßig übereinandergestapelt, wenn Aktivitäten aus einer Aktivität im selben sekundären Container gestartet werden. Aktivitäten, die vom primären Container innerhalb eines aktiven Splits gestartet wurden, landen auch im sekundären Container ganz oben im Aktivitätsstapel.

Aktivitäten in einer neuen Aufgabe

Wenn Aktivitäten in einem Fenster mit geteilter Aufgabe Aktivitäten in einer neuen Aufgabe starten, wird die neue Aufgabe von der Aufgabe getrennt, die die Aufteilung enthält, und im gesamten Fenster angezeigt. Auf dem Bildschirm „Zuletzt“ werden zwei Aufgaben angezeigt: die Aufgabe in der geteilten Ablage und die neue Aufgabe.

Abbildung 20. Starten Sie Aktivität C in einer neuen Aufgabe auf Aktivität B.

Aktivitäten ersetzen

Aktivitäten können im sekundären Containerstack ersetzt werden, z. B. wenn die primäre Aktivität für die Navigation auf oberster Ebene verwendet wird und die sekundäre Aktivität ein ausgewähltes Ziel ist. Mit jeder Auswahl in der Navigation der obersten Ebene sollte eine neue Aktivität im sekundären Container gestartet und die zuvor vorhandenen Aktivitäten entfernt werden.

Abbildung 21. Die Navigationsaktivität der obersten Ebene im primären Bereich ersetzt die Zielaktivitäten im sekundären Bereich.

Wenn die App die Aktivität im sekundären Container nicht beendet, wenn sich die Navigationsauswahl ändert, könnte die Zurück-Navigation verwirrend sein, wenn die Teilung minimiert wird (wenn das Gerät zugeklappt ist). Wenn Sie beispielsweise im Hauptbereich ein Menü haben und im zweiten Bereich die Bildschirme A und B gestapelt sind, befindet sich B auf A und A über dem Menü, wenn der Nutzer das Smartphone zusammenklappt. Wenn Nutzende von B zurücknavigieren, wird anstelle des Menüs A angezeigt.

In diesem Fall muss Bildschirm A aus dem hinteren Bereich entfernt werden.

Das Standardverhalten beim Starten von Seiten in einem neuen Container über einem vorhandenen Split besteht darin, die neuen sekundären Container oben zu platzieren und die alten im Back-Stack beizubehalten. Sie können die Aufteilungen so konfigurieren, dass die vorherigen sekundären Container mit clearTop gelöscht und neue Aktivitäten normal gestartet werden.

<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)));
    }
}

Alternativ können Sie dieselbe sekundäre Aktivität verwenden und über die primäre Aktivität (Menü) neue Intents senden, die in dieselbe Instanz aufgelöst werden, aber ein Status- oder UI-Update im sekundären Container auslösen.

Mehrere Splits

Anwendungen können eine mehrstufige tiefe Navigation bieten, indem zusätzliche Aktivitäten neben der Seite gestartet werden.

Wenn eine Aktivität in einem sekundären Container eine neue Aktivität an der Seite startet, wird über die vorhandene Aufteilung ein neuer Split erstellt.

Abbildung 22. Aktivität B startet neben Aktivität C.

Der Back Stack enthält alle Aktivitäten, die zuvor geöffnet wurden. Nutzer können also nach Abschluss von C zum A/B-Split wechseln.

Die Aktivitäten A, B und C in einem Stapel. Die Aktivitäten sind in der folgenden Reihenfolge von oben nach unten gestapelt: C, B, A.

Um einen neuen Split zu erstellen, starten Sie die neue Aktivität neben dem vorhandenen sekundären Container. Geben Sie die Konfigurationen für den A/B- und den B/C-Split an und starten Sie Aktivität C normal aus 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));
    }
}

Auf Änderungen des Split-Status reagieren

Verschiedene Aktivitäten in einer App können UI-Elemente haben, die dieselbe Funktion ausführen, z. B. ein Steuerelement, das ein Fenster mit Kontoeinstellungen öffnet.

Abbildung 23. Verschiedene Aktivitäten mit funktional identischen UI-Elementen

Wenn zwei Aktivitäten, die ein UI-Element gemeinsam haben, in einem Split enthalten sind, ist es redundant und möglicherweise verwirrend, das Element in beiden Aktivitäten anzuzeigen.

Abbildung 24. Doppelte UI-Elemente bei der Aktivitätsaufteilung.

Wenn Sie wissen möchten, wann Aktivitäten geteilt werden, prüfen Sie den SplitController.splitInfoList-Ablauf oder registrieren Sie einen Listener mit SplitControllerCallbackAdapter auf Änderungen am geteilten Status. Passen Sie dann die Benutzeroberfläche entsprechend an:

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);
            });
}

Coroutinen können in jedem Lebenszyklusstatus gestartet werden. Normalerweise werden sie jedoch mit dem Status STARTED gestartet, um Ressourcen zu schonen. Weitere Informationen finden Sie unter Kotlin-Koroutinen mit Komponenten verwenden, die den Lebenszyklus berücksichtigen.

Callbacks können in jedem Lebenszyklusstatus erfolgen, auch wenn eine Aktivität angehalten wird. Listener sollten normalerweise in onStart() registriert und in onStop() nicht registriert sein.

Modales Fenster im Vollbildmodus

Einige Aktivitäten hindern Nutzer daran, mit der Anwendung zu interagieren, bis eine bestimmte Aktion ausgeführt wird, z. B. eine Aktivität auf dem Anmeldebildschirm, ein Bildschirm zur Bestätigung einer Richtlinie oder eine Fehlermeldung. Modale Aktivitäten sollten in einem Split nicht angezeigt werden.

Mit der Konfiguration zum Maximieren kann eine Aktivität gezwungen werden, immer das Aufgabenfenster zu füllen:

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

Aktivitäten beenden

Nutzer können Aktivitäten auf beiden Seiten des Splits beenden, indem sie vom Displayrand wischen:

Abbildung 25. Wischgeste zum Beenden von Aktivität B.
Abbildung 26. Wische mit einer Touch-Geste zum Beenden von Aktivität A.

Wenn das Gerät so eingerichtet ist, dass die Schaltfläche „Zurück“ anstelle der Bedienung über Gesten verwendet wird, wird die Eingabe an die fokussierte Aktivität gesendet – die Aktivität, die zuletzt berührt oder gestartet wurde.

Wie sich das Beenden aller Aktivitäten in einem Container auf den anderen Container auswirkt, hängt von der Split-Konfiguration ab.

Konfigurationsattribute

Sie können Regelattribute für Aufteilungspaare angeben, um zu konfigurieren, wie sich der Abschluss aller Aktivitäten auf einer Seite der Aufteilung auf die Aktivitäten auf der anderen Seite der Aufteilung auswirkt. Folgende Attribute sind verfügbar:

  • window:finishPrimaryWithSecondary: Wie wirkt sich das Abschließen aller Aktivitäten im sekundären Container auf die Aktivitäten im primären Container aus?
  • window:finishSecondaryWithPrimary: Wie sich ein Abschluss aller Aktivitäten im primären Container auf die Aktivitäten im sekundären Container auswirkt

Mögliche Werte für die Attribute sind:

  • always: Beenden Sie immer die Aktivitäten im zugehörigen Container.
  • never: Die Aktivitäten im verknüpften Container werden nie abgeschlossen.
  • adjacent: beendet die Aktivitäten im zugehörigen Container, wenn die beiden Container nebeneinander angezeigt werden, aber nicht, wenn die beiden Container gestapelt sind

Beispiele:

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

Standardkonfiguration

Wenn alle Aktivitäten in einem Container eines geteilten Fensters abgeschlossen sind, belegt der verbleibende Container das gesamte Fenster:

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

Aufteilung mit Aktivitäten A und B. A ist beendet, sodass B das gesamte Fenster einnimmt.

Aufteilung mit Aktivitäten A und B. B ist fertig, sodass A das gesamte Fenster einnimmt.

Aktivitäten gemeinsam beenden

Sie beenden die Aktivitäten im primären Container automatisch, wenn alle Aktivitäten im sekundären Container abgeschlossen sind:

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

Aufteilung mit Aktivitäten A und B. B ist abgeschlossen, was ebenfalls A beendet und das Aufgabenfenster leer lässt.

Aufteilung mit Aktivitäten A und B. A ist abgeschlossen und B bleibt im Aufgabenfenster unverändert.

Sie beenden die Aktivitäten im sekundären Container automatisch, wenn alle Aktivitäten im primären Container abgeschlossen sind:

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

Aufteilung mit Aktivitäten A und B. A ist abgeschlossen, was ebenfalls B beendet und das Aufgabenfenster leer lässt.

Aufteilung mit Aktivitäten A und B. B ist fertig und lässt A im Aufgabenfenster unverändert.

Aktivitäten gemeinsam beenden, wenn alle Aktivitäten im primären oder im sekundären Container abgeschlossen sind:

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

Aufteilung mit Aktivitäten A und B. A ist abgeschlossen, was ebenfalls B beendet und das Aufgabenfenster leer lässt.

Aufteilung mit Aktivitäten A und B. B ist abgeschlossen, was ebenfalls A beendet und das Aufgabenfenster leer lässt.

Mehrere Aktivitäten in Containern beenden

Wenn mehrere Aktivitäten in einem geteilten Container gestapelt sind, werden die oben auf dem Stapel befindlichen Aktivitäten nicht automatisch beendet, wenn eine Aktivität am unteren Rand des Stapels abgeschlossen wird.

Wenn sich beispielsweise zwei Aktivitäten im sekundären Container mit C über B befinden:

Der sekundäre Aktivitätsstapel mit Aktivität C, der über B gestapelt ist, wird über den ersten Aktivitätsstapel gestapelt, der Aktivität A enthält.

und die Konfiguration der Aufteilung durch die Konfiguration der Aktivitäten A und B definiert wird:

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

durch den Abschluss der Top-Aktivität bleibt die Teilung erhalten.

Teilen Sie mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. C wird beendet, sodass A und B in der Aktivitätsaufteilung verbleiben.

Durch das Beenden der unteren (Stamm-)Aktivität des sekundären Containers werden die darauf befindlichen Aktivitäten nicht entfernt und die Aufteilung bleibt somit erhalten.

Teilen Sie mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. B wird beendet, sodass A und C in der Aktivitätsaufteilung verbleiben.

Alle zusätzlichen Regeln für den gemeinsamen Abschluss von Aktivitäten, wie das Abschließen der sekundären Aktivität mit der primären Aktivität, werden ebenfalls ausgeführt:

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

Aufteilen mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. A wird beendet und dann B und C abgeschlossen.

Und wenn die Aufteilung so konfiguriert ist, dass der primäre und der sekundäre Dienst zusammen beendet werden:

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

Teilen Sie mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. C wird beendet, sodass A und B in der Aktivitätsaufteilung verbleiben.

Teilen Sie mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. B wird beendet, sodass A und C in der Aktivitätsaufteilung verbleiben.

Teilen Sie mit Aktivität A im primären Container und Aktivitäten B und C im sekundären Container, Aktivität C über B. A wird beendet und dann B und C abgeschlossen.

Aufteilungseigenschaften zur Laufzeit ändern

Die Eigenschaften einer derzeit aktiven und sichtbaren Aufteilung können nicht geändert werden. Änderungen an den Aufteilungsregeln wirken sich auf zusätzliche Aktivitätsstarts und neue Container aus, jedoch nicht auf vorhandene und aktive Aufteilungen.

Wenn Sie die Attribute aktiver Splits ändern möchten, beenden Sie die Nebenaktivität oder Aktivitäten im Split und starten Sie die Seite noch einmal mit einer neuen Konfiguration.

Aktivität aus einem Split in ein vollständiges Fenster extrahieren

Erstellen Sie eine neue Konfiguration, bei der das vollständige Fenster der Nebenaktivität angezeigt wird, und starten Sie die Aktivität dann mit einem Intent neu, der in dieselbe Instanz aufgelöst wird.

Während der Laufzeit auf geteilte Unterstützung prüfen

Das Einbetten von Aktivitäten wird unter Android 12L (API-Level 32) und höher unterstützt, ist aber auch auf einigen Geräten mit älteren Plattformversionen verfügbar. Mit der Property SplitController.splitSupportStatus oder der Methode SplitController.getSplitSupportStatus() können Sie während der Laufzeit prüfen, ob die Funktion verfügbar ist:

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

Wenn Aufteilungen nicht unterstützt werden, werden Aktivitäten zusätzlich zum Aktivitätsstapel gestartet (gemäß dem Einbettungsmodell ohne Aktivitäten).

Überschreiben des Systems verhindern

Die Hersteller von Android-Geräten wie Erstausrüster (Original Equipment Manufacturer, OEMs) können die Aktivitätseinbettung als Funktion des Gerätesystems implementieren. Das System gibt Aufteilungsregeln für Anwendungen mit mehreren Aktivitäten vor und überschreibt so das Windowing-Verhalten der Anwendungen. Durch die Systemüberschreibung werden Apps mit mehreren Aktivitäten in einen systemdefinierten Aktivitätseinbettungsmodus gezwungen.

Durch das Einbetten von Systemaktivitäten kann die Darstellung der App durch Layouts mit mehreren Bereichen wie list-detail verbessert werden, ohne dass Änderungen an der App erforderlich sind. Die Aktivitätseinbettung des Systems kann jedoch auch zu falschen App-Layouts, Fehlern oder Konflikten mit der von der App implementierten Aktivitätseinbettung führen.

Durch Festlegen eines Attributs in der Manifestdatei der App lässt sich das Einbetten von Systemaktivitäten verhindern oder zulassen. Beispiel:

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

Der Name der Eigenschaft wird im WindowProperties-Objekt von Jetpack WindowManager definiert. Legen Sie den Wert auf false fest, wenn Ihre App die Aktivitätseinbettung implementiert oder wenn Sie anderweitig verhindern möchten, dass das System seine Regeln zum Einbetten von Aktivitäten auf Ihre App anwendet. Setzen Sie den Wert auf true, damit das System die systemdefinierte Aktivitätseinbettung auf Ihre App anwenden kann.

Einschränkungen, Einschränkungen und Vorbehalte

  • Nur die Host-App der Aufgabe, die als Inhaber der Stammaktivität in der Aufgabe angegeben ist, kann andere Aktivitäten organisieren und in die Aufgabe einbetten. Wenn Aktivitäten, die Einbettungen und Aufteilungen unterstützen, in einer Aufgabe ausgeführt werden, die zu einer anderen Anwendung gehört, funktionieren Einbettungen und Aufteilungen für diese Aktivitäten nicht.
  • Aktivitäten können nur innerhalb einer einzelnen Aufgabe organisiert werden. Wenn Sie eine Aktivität in einer neuen Aufgabe starten, wird sie immer in einem neuen maximierten Fenster außerhalb der vorhandenen Aufteilungen platziert.
  • Nur Aktivitäten im selben Prozess können organisiert und aufgeteilt werden. Der Callback SplitInfo meldet nur Aktivitäten, die zum selben Prozess gehören, da es keine Informationen über Aktivitäten in verschiedenen Prozessen gibt.
  • Jede Paarregel oder einzelne Aktivitätsregel gilt nur für Aktivitätsstarts, die nach der Registrierung der Regel erfolgen. Derzeit gibt es keine Möglichkeit, vorhandene Aufteilungen oder ihre visuellen Eigenschaften zu aktualisieren.
  • Die Konfiguration des Split-Paar-Filters muss mit den Intents übereinstimmen, die beim vollständigen Starten von Aktivitäten verwendet werden. Der Abgleich erfolgt an dem Punkt, an dem eine neue Aktivität aus dem Anwendungsprozess gestartet wird. Komponentennamen, die bei Verwendung impliziter Intents später im Systemprozess aufgelöst werden, sind demnach möglicherweise nicht bekannt. Wenn der Name einer Komponente zum Zeitpunkt des Starts nicht bekannt ist, kann stattdessen ein Platzhalter („*/*“) verwendet und eine Filterung basierend auf der Intent-Aktion durchgeführt werden.
  • Es gibt derzeit keine Möglichkeit, Aktivitäten zwischen Containern oder in und aus Splits zu verschieben, nachdem sie erstellt wurden. Splits werden nur von der WindowManager-Bibliothek erstellt, wenn neue Aktivitäten mit übereinstimmenden Regeln gestartet werden, und Splits werden gelöscht, wenn die letzte Aktivität in einem Aufteilungscontainer abgeschlossen ist.
  • Aktivitäten können bei einer Konfigurationsänderung neu gestartet werden. Wenn also ein Split erstellt oder entfernt wird und sich die Aktivitätsgrenzen ändern, kann die Aktivität vollständig gelöscht und die neue Instanz erstellt werden. Daher sollten App-Entwickler bei Dingen wie dem Starten neuer Aktivitäten aus Lebenszyklus-Callbacks vorsichtig sein.
  • Geräte müssen die Oberfläche für Fenstererweiterungen enthalten, damit Aktivitäten eingebettet werden können. Fast alle Geräte mit großen Bildschirmen, auf denen Android 12L (API-Level 32) oder höher ausgeführt wird, verfügen über die Schnittstelle. Einige Geräte mit großen Bildschirmen, die nicht mehrere Aktivitäten ausführen können, enthalten jedoch die Oberfläche für Fenstererweiterungen nicht. Wenn ein Gerät mit großen Bildschirmen den Mehrfenstermodus nicht unterstützt, wird das Einbetten von Aktivitäten möglicherweise nicht unterstützt.

Weitere Informationen