Durch die Einbettung von Aktivitäten werden Apps auf Geräten mit großen Bildschirmen optimiert, indem das Aufgabenfenster einer Anwendung zwischen zwei Aktivitäten oder zwei Instanzen derselben Aktivität aufgeteilt wird.
Wenn Ihre App aus mehreren Aktivitäten besteht, können Sie 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 kein Refactoring des Codes erforderlich. Sie legen fest, wie die Aktivitäten Ihrer App angezeigt werden – nebeneinander oder gestapelt –, indem Sie eine XML-Konfigurationsdatei erstellen oder Jetpack WindowManager-API-Aufrufe ausführen.
Die Unterstützung für kleine Bildschirme wird automatisch aufrechterhalten. Wenn Ihre App auf einem Gerät mit kleinem Display ausgeführt wird, werden Aktivitäten übereinander gestapelt. Auf großen Bildschirmen werden Aktivitäten nebeneinander angezeigt. Das System bestimmt die Darstellung anhand der von Ihnen erstellten Konfiguration. Es ist keine Verzweigungslogik erforderlich.
Bei der Einbettung von Aktivitäten werden Änderungen der Geräteausrichtung berücksichtigt. Sie funktioniert nahtlos auf faltbaren Geräten, wobei Aktivitäten gestapelt und entstapelt werden, wenn das Gerät gefaltet und entfaltet wird.
Das Einbetten von Aktivitäten wird auf den meisten Geräten mit großem Display unterstützt, auf denen Android 12L (API‑Level 32) und höher ausgeführt wird.
Aufgabenfenster aufteilen
Bei der Einbettung von Aktivitäten wird das App-Aufgabenfenster in zwei Container aufgeteilt: primär und sekundär. Die Container enthalten Aktivitäten, die über die Hauptaktivität oder über andere Aktivitäten in den Containern gestartet wurden.
Aktivitäten werden im sekundären Container gestapelt, wenn sie gestartet werden. Auf kleinen Bildschirmen wird der sekundäre Container über dem primären Container gestapelt. Das Stapeln von Aktivitäten und die Rückwärtsnavigation entsprechen also der Reihenfolge der Aktivitäten, die bereits in Ihre App integriert sind.
Mit der Einbettung von Aktivitäten können Sie Aktivitäten auf verschiedene Arten präsentieren. Ihre App kann das Aufgabenfenster aufteilen, indem sie zwei Aktivitäten gleichzeitig nebeneinander oder übereinander startet:
Wenn eine Aktivität das gesamte Aufgabenfenster einnimmt, kann sie durch Starten einer neuen Aktivität daneben aufgeteilt werden:
Aktivitäten, die sich bereits in einem Split befinden und ein Aufgabenfenster gemeinsam nutzen, können andere Aktivitäten auf folgende Weise starten:
Neben einer anderen Aktivität:
Abbildung 4: Aktivität A startet Aktivität C seitlich über Aktivität B. Zur Seite und verschieben Sie die Aufteilung seitlich, um die vorherige primäre Aktivität zu verbergen:
Abbildung 5: Aktivität B startet Aktivität C seitlich und verschiebt die Aufteilung seitlich. Starten Sie eine Aktivität an Ort und Stelle oben im selben Aktivitätsstack:
Abbildung 6: Aktivität B startet Aktivität C ohne zusätzliche Intent-Flags. Aktivität im Vollbildmodus in derselben Aufgabe starten:
Abbildung 7. Aktivität A oder Aktivität B startet Aktivität C, die das Aufgabenfenster ausfüllt.
Rückwärtsnavigation
Je nach Abhängigkeiten zwischen Aktivitäten oder der Art und Weise, wie Nutzer das Zurück-Ereignis auslösen, können verschiedene Arten von Anwendungen unterschiedliche Regeln für die Zurück-Navigation in einem geteilten Aufgabenfenster haben, z. B.:
- Gemeinsam zurückgehen: Wenn Aktivitäten miteinander verknüpft sind und eine nicht ohne die andere angezeigt werden sollte, kann die Zurück-Navigation so konfiguriert werden, dass beide beendet werden.
- Allein: Wenn Aktivitäten vollständig unabhängig sind, wirkt sich die Zurück-Navigation in einer Aktivität nicht auf den Status einer anderen Aktivität im Aufgabenfenster aus.
Das „Zurück“-Ereignis wird bei Verwendung der Schaltflächennavigation an die zuletzt fokussierte Aktivität gesendet.
Für die Bedienung über Gesten:
Android 14 (API‑Level 34) und niedriger: Das „Zurück“-Ereignis wird an die Aktivität gesendet, in der die Geste ausgeführt wurde. Wenn Nutzer vom linken Bildschirmrand wischen, wird das „Zurück“-Ereignis an die Aktivität im linken Bereich des geteilten Fensters gesendet. Wenn Nutzer vom rechten Bildschirmrand wischen, wird das Ereignis „Zurück“ an die Aktivität im rechten Bereich gesendet.
Android 15 (API‑Level 35) und höher
Wenn Sie mehrere Aktivitäten derselben App verwenden, wird durch die Geste die oberste Aktivität beendet, unabhängig von der Wischrichtung. Das sorgt für eine einheitlichere Bedienung.
Bei Szenarien mit zwei Aktivitäten aus verschiedenen Apps (Overlay) wird das Zurück-Ereignis an die letzte Aktivität im Fokus weitergeleitet, was dem Verhalten der Schaltflächennavigation entspricht.
Layout mit mehreren Bereichen
Mit Jetpack WindowManager können Sie ein Layout mit mehreren Bereichen für die Aktivitätseinbettung auf Geräten mit großem Display mit Android 12L (API-Level 32) oder höher und auf einigen Geräten mit früheren Plattformversionen erstellen. Bei vorhandenen Apps, die auf mehreren Aktivitäten anstelle von Fragmenten oder ansichtsbasierten Layouts wie SlidingPaneLayout basieren, kann die Nutzerfreundlichkeit auf großen Bildschirmen verbessert werden, ohne dass der Quellcode umgestaltet werden muss.
Ein häufiges Beispiel ist die Aufteilung in Liste und Detailansicht. Um eine hochwertige Präsentation zu gewährleisten, startet das System die Listenaktivität und die Anwendung startet dann sofort die Detailaktivität. Das Übergangssystem wartet, bis beide Aktivitäten gezeichnet wurden, und zeigt sie dann zusammen an. Für den Nutzer werden die beiden Aktivitäten als eine gestartet.
Aufteilungsattribute
Sie können festlegen, wie das Aufgabenfenster zwischen den geteilten Containern aufgeteilt wird und wie die Container relativ zueinander angeordnet werden.
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 werden. Mögliche Werte:ltr: Von links nach rechtsrtl: Linksläufiglocale: Entwederltroderrtlwird anhand der Ländereinstellung bestimmt.
Beispiele finden Sie 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:
setSplitType(): Legt die Anteile der aufgeteilten Container fest. Informationen zu gültigen Argumenten, einschließlich der MethodeSplitAttributes.SplitType.ratio(), finden Sie unterSplitAttributes.SplitType.setLayoutDirection(): Damit wird das Layout der Container festgelegt. Mögliche Werte finden Sie unterSplitAttributes.LayoutDirection.
Beispiele finden Sie im Abschnitt WindowManager API.
Ausrichtung der Aufteilung
Die Abmessungen und das Seitenverhältnis des Displays bestimmen die Positionierung von Aktivitäten in Splits für die Einbettung von Aktivitäten. Auf großen Displays im Querformat werden Aktivitäten nebeneinander angezeigt, auf hohen Displays im Hochformat oder im Tischmodus auf Faltgeräten übereinander.
Mit dem Rechner SplitController
SplitAttributes können Sie die Ausrichtung der Aufteilung angeben. Mit dem Rechner wird SplitAttributes für die aktive SplitRule berechnet.
Mit dem Rechner können Sie den übergeordneten Container für verschiedene Gerätestatus in unterschiedliche Richtungen aufteilen, z. B.:
Kotlin
if (WindowSdkExtensions.getInstance().extensionVersion >= 2) { SplitController.getInstance(this).setSplitAttributesCalculator { params -> val parentConfiguration = params.parentConfiguration val builder = SplitAttributes.Builder() return@setSplitAttributesCalculator if (parentConfiguration.screenWidthDp >= 840) { // Side-by-side dual-pane layout for wide displays. builder .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) .build() } else if (parentConfiguration.screenHeightDp >= 600) { // Horizontal split for tall displays. builder .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP) .build() } else { // Fallback to expand the secondary container. builder .setSplitType(SPLIT_TYPE_EXPAND) .build() } } }
Java
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) { SplitController.getInstance(this).setSplitAttributesCalculator(params -> { Configuration parentConfiguration = params.getParentConfiguration(); SplitAttributes.Builder builder = new SplitAttributes.Builder(); if (parentConfiguration.screenWidthDp >= 840) { // Side-by-side dual-pane layout for wide displays. return builder .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) .build(); } else if (parentConfiguration.screenHeightDp >= 600) { // Horizontal split for tall displays. return builder .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP) .build(); } else { // Fallback to expand the secondary container. return builder .setSplitType(SplitType.SPLIT_TYPE_EXPAND) .build(); } }); }
Auf faltbaren Geräten können Sie den Bildschirm vertikal teilen, wenn sich das Gerät im Querformat befindet, eine einzelne Aktivität anzeigen, wenn sich das Gerät im Hochformat befindet, und den Bildschirm horizontal teilen, wenn sich das Gerät im Tischmodus befindet:
Kotlin
if (WindowSdkExtensions.getInstance().extensionVersion >= 2) { SplitController.getInstance(this).setSplitAttributesCalculator { params -> val tag = params.splitRuleTag val parentWindowMetrics = params.parentWindowMetrics val parentConfiguration = params.parentConfiguration val foldingFeatures = params.parentWindowLayoutInfo.displayFeatures.filterIsInstance<FoldingFeature>() val feature = if (foldingFeatures.size == 1) foldingFeatures[0] else null val builder = SplitAttributes.Builder() builder.setSplitType(SPLIT_TYPE_HINGE) return@setSplitAttributesCalculator if (feature?.isSeparating == true) { // Horizontal split for tabletop posture. builder .setSplitType(SPLIT_TYPE_HINGE) .setLayoutDirection( if (feature.orientation == FoldingFeature.Orientation.HORIZONTAL) { SplitAttributes.LayoutDirection.BOTTOM_TO_TOP } else { SplitAttributes.LayoutDirection.LOCALE } ) .build() } else if (parentConfiguration.screenWidthDp >= 840) { // Side-by-side dual-pane layout for wide displays. builder .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) .build() } else { // No split for tall displays. builder .setSplitType(SPLIT_TYPE_EXPAND) .build() } } }
Java
if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 2) { SplitController.getInstance(this).setSplitAttributesCalculator(params -> { String tag = params.getSplitRuleTag(); WindowMetrics parentWindowMetrics = params.getParentWindowMetrics(); Configuration parentConfiguration = params.getParentConfiguration(); List<FoldingFeature> foldingFeatures = params.getParentWindowLayoutInfo().getDisplayFeatures().stream().filter( item -> item instanceof FoldingFeature) .map(item -> (FoldingFeature) item) .collect(Collectors.toList()); FoldingFeature feature = foldingFeatures.size() == 1 ? foldingFeatures.get(0) : null; SplitAttributes.Builder builder = new SplitAttributes.Builder(); builder.setSplitType(SplitType.SPLIT_TYPE_HINGE); if (feature != null && feature.isSeparating()) { // Horizontal slit for tabletop posture. return builder .setSplitType(SplitType.SPLIT_TYPE_HINGE) .setLayoutDirection( feature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL ? SplitAttributes.LayoutDirection.BOTTOM_TO_TOP : SplitAttributes.LayoutDirection.LOCALE) .build(); } else if (parentConfiguration.screenWidthDp >= 840) { // Side-by-side dual-pane layout for wide displays. return builder .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE) .build(); } else { // No split for tall displays. return builder .setSplitType(SplitType.SPLIT_TYPE_EXPAND) .build(); } }); }
Platzhalter
Platzhalteraktivitäten sind leere sekundäre Aktivitäten, die einen Bereich eines Aktivitätssplits belegen. Sie sollen letztendlich durch eine andere Aktivität ersetzt werden, die Inhalte enthält. Beispielsweise könnte eine Platzhalteraktivität die sekundäre Seite einer Aktivität in einem Listendetail-Layout belegen, bis ein Element aus der Liste ausgewählt wird. Dann wird die Platzhalteraktivität durch eine Aktivität mit den Detailinformationen für das ausgewählte Listenelement ersetzt.
Standardmäßig werden Platzhalter nur angezeigt, wenn genügend Platz für eine Aufteilung der Aktivität vorhanden ist. Platzhalter werden automatisch beendet, wenn die Anzeigegröße auf eine Breite oder Höhe geändert wird, die zu klein ist, um einen Split darzustellen. Wenn genügend Speicherplatz vorhanden ist, wird der Platzhalter mit einem neu initialisierten Status neu gestartet.
Das Standardverhalten kann jedoch durch das Attribut stickyPlaceholder einer SplitPlaceholderRule- oder setSticky()-Methode von SplitPlaceholder.Builder überschrieben werden. Wenn für das Attribut oder die Methode der Wert true angegeben ist, wird der Platzhalter als oberste Aktivität im Aufgabenfenster angezeigt, wenn die Anzeige von einer zweispaltigen zu einer einspaltigen Anzeige verkleinert wird (siehe Aufteilungskonfiguration für ein Beispiel).
Änderungen der Fenstergröße
Wenn sich die Gerätekonfiguration ändert und dadurch die Breite des Taskfensters so verringert wird, dass sie für ein Layout mit mehreren Bereichen nicht mehr ausreicht (z. B. wenn ein faltbares Gerät mit großem Display von Tablet- auf Smartphone-Größe zusammengefaltet wird oder die Größe des App-Fensters im Mehrfenstermodus geändert wird), werden die Aktivitäten ohne Platzhalter im sekundären Bereich des Taskfensters über den Aktivitäten im primären Bereich gestapelt.
Platzhalteraktivitäten werden nur angezeigt, wenn die Anzeigebreite für eine Aufteilung ausreicht. Auf kleineren Bildschirmen wird der Platzhalter automatisch geschlossen. Wenn der Anzeigebereich wieder groß genug ist, wird der Platzhalter neu erstellt. Weitere Informationen finden Sie im Abschnitt Platzhalter.
Das Stapeln von Aktivitäten ist möglich, weil WindowManager die Aktivitäten im sekundären Bereich über den Aktivitäten im primären Bereich anordnet.
Mehrere Aktivitäten im sekundären Bereich
Aktivität B startet Aktivität C ohne zusätzliche Intent-Flags:

Dies führt zu der folgenden Z-Reihenfolge von Aktivitäten in derselben Aufgabe:

In einem kleineren Aufgabenfenster wird die Anwendung also auf eine einzelne Aktivität mit C oben im Stapel reduziert:

Wenn Sie im kleineren Fenster zurückgehen, wird durch die Aktivitäten navigiert, die übereinander gestapelt sind.
Wenn die Konfiguration des Aufgabenfensters auf eine größere Größe zurückgesetzt wird, in der mehrere Bereiche Platz finden, werden die Aktivitäten wieder nebeneinander angezeigt.
Gestapelte Splits
Bei Aktivität B wird Aktivität C seitlich gestartet und die Aufteilung wird seitlich verschoben:

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

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

Feste Ausrichtung im Hochformat
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 Foldables zu verbessern, können Gerätehersteller (OEMs) Anfragen zur Bildschirmausrichtung ignorieren und die App auf Displays im Querformat im Hochformat oder auf Displays im Hochformat im Querformat letterboxen.
Wenn die Einbettung von Aktivitäten aktiviert ist, können OEMs Geräte so anpassen, dass Aktivitäten im Hochformat mit festem Seitenverhältnis auf großen Bildschirmen (Breite ≥ 600 dp) im Querformat letterboxiert werden. Wenn eine Aktivität im Hochformat eine zweite Aktivität startet, kann das Gerät die beiden Aktivitäten nebeneinander in einer zweispaltigen Anzeige darstellen.
Fügen Sie der App-Manifestdatei immer das Attribut android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED hinzu, um Geräte darüber zu informieren, dass Ihre App das Einbetten von Aktivitäten unterstützt (siehe Abschnitt Aufteilungskonfiguration). Auf OEM-angepassten Geräten kann dann festgelegt werden, ob Aktivitäten im Hochformat mit festem Seitenverhältnis letterboxed werden sollen.
Konfiguration der Aufteilung
Mit Split-Regeln werden Aktivitäts-Splits konfiguriert. Sie definieren Aufteilungsregeln in einer XML-Konfigurationsdatei oder durch Aufrufe der Jetpack WindowManager API.
In beiden Fällen muss Ihre App auf die WindowManager-Bibliothek zugreifen und dem System mitteilen, dass die App die Einbettung von Aktivitäten implementiert hat.
Unternimm Folgendes:
Fügen Sie die aktuelle WindowManager-Bibliotheksabhängigkeit der Datei
build.gradleauf Modulebene Ihrer App hinzu, z. B.:implementation 'androidx.window:window:1.1.0-beta02'Die WindowManager-Bibliothek enthält alle Komponenten, die für das Einbetten von Aktivitäten erforderlich sind.
Informieren Sie das System darüber, dass Ihre App die Einbettung von Aktivitäten implementiert hat.
Fügen Sie dem <application>-Element der App-Manifestdatei das Attribut
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLEDhinzu und legen Sie den Wert auf „true“ fest, z. B.:<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 Splits für die Einbettung von Aktivitäten deaktiviert, sofern das Attribut nicht dem Manifest hinzugefügt und auf „true“ gesetzt wurde.
Außerdem verwenden Gerätehersteller die Einstellung, um benutzerdefinierte Funktionen für Apps zu aktivieren, die das Einbetten von Aktivitäten unterstützen. Geräte können beispielsweise eine Aktivität, die nur im Hochformat verfügbar ist, auf Displays im Querformat letterboxen, um die Aktivität für den Übergang zu einem zweispaltigen Layout auszurichten, wenn eine zweite Aktivität gestartet wird (siehe Feste Ausrichtung im Hochformat).
XML-Konfiguration
So erstellen Sie eine XML-basierte Implementierung der Einbettung von Aktivitäten:
Erstellen Sie eine XML-Ressourcendatei, die Folgendes tut:
- Definiert Aktivitäten, die einen Split gemeinsam nutzen
- Konfiguriert die Aufteilungsoptionen.
- Erstellt einen Platzhalter für den sekundären Container des Splits, wenn keine Inhalte verfügbar sind.
- Gibt Aktivitäten an, die niemals Teil einer Aufteilung sein sollten.
Beispiel:
<!-- 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>Erstellen Sie einen Initializer.
Die WindowManager-Komponente
RuleControllerparst die XML-Konfigurationsdatei und stellt die Regeln für das System zur Verfügung. Eine Jetpack-Startup-BibliothekInitializermacht die XML-Datei beim App-Start fürRuleControllerverfügbar, sodass die Regeln beim Start von Aktivitäten gelten.So erstellen Sie einen Initialisierer:
Fügen Sie die aktuelle Jetpack Startup-Bibliotheksabhängigkeit der Datei
build.gradleauf Modulebene hinzu, z. B.:implementation 'androidx.startup:startup-runtime:1.1.1'Erstellen Sie eine Klasse, die die
Initializer-Schnittstelle implementiert.Der Initialisierer stellt die Aufteilungsregeln für
RuleControllerzur Verfügung, indem er die ID der XML-Konfigurationsdatei (main_split_config.xml) an die MethodeRuleController.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(); } }
Erstellen Sie einen Contentanbieter für die Regeldefinitionen.
Fügen Sie
androidx.startup.InitializationProviderals<provider>in die App-Manifestdatei ein. Fügen Sie einen Verweis auf die Implementierung IhresRuleController-InitialisierersSplitInitializerein:<!-- 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>InitializationProvidererkennt und initialisiertSplitInitializer, bevor dieonCreate()-Methode der App aufgerufen wird. Die Aufteilungsregeln werden also wirksam, wenn die Hauptaktivität der App gestartet wird.
WindowManager API
Sie können die Einbettung von Aktivitäten programmatisch mit einigen API-Aufrufen implementieren. Rufen Sie die Methoden in der onCreate()-Methode einer Unterklasse von Application auf, damit die Regeln vor dem Start von Aktivitäten gelten.
So erstellen Sie programmatisch eine Aktivitätsaufteilung:
So erstellen Sie eine Aufteilungsregel:
Erstellen Sie ein
SplitPairFilter, das die Aktivitäten identifiziert, die den Split gemeinsam nutzen: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 );
Fügen Sie den Filter einem Filtersatz hinzu:
```Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
Layoutattribute für die Aufteilung erstellen:
Kotlin
val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build()
Java
SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build();
Mit
SplitAttributes.Builderwird ein Objekt mit Layoutattributen erstellt:setSplitType(): Definiert, wie der verfügbare Anzeigebereich den einzelnen Aktivitätscontainern zugewiesen wird. Der Typ der Aufteilung des Verhältnisses 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 relativ zueinander angeordnet werden, wobei der primäre Container zuerst kommt.
SplitPairRuleerstellen: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.Builderwird die Regel erstellt und konfiguriert:filterSet: Enthält Filter für geteilte Paare, mit denen bestimmt wird, wann die Regel angewendet werden soll. Dazu werden Aktivitäten identifiziert, die einen Split gemeinsam haben.setDefaultSplitAttributes(): Wendet Layoutattribute auf die Regel an.setMinWidthDp(): Legt die Mindestbreite des Displays (in dichteunabhängigen Pixeln, dp) fest, die eine Aufteilung ermöglicht.setMinSmallestWidthDp(): Legt den Mindestwert (in dp) fest, den die kleinere der beiden Displaydimensionen haben muss, um eine Aufteilung unabhängig von der Geräteausrichtung zu ermöglichen.setMaxAspectRatioInPortrait(): Legt das maximale Seitenverhältnis (Höhe:Breite) im Hochformat fest, für das Aktivitätsteile angezeigt werden. Wenn das Seitenverhältnis eines Displays im Hochformat das maximale Seitenverhältnis überschreitet, werden Splits unabhängig von der Breite des Displays deaktiviert. Hinweis:Der Standardwert ist 1, 4.Dadurch nehmen Aktivitäten auf den meisten Tablets im Hochformat das gesamte Aufgabenfenster ein. Siehe auchSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULTundsetMaxAspectRatioInLandscape(). Der Standardwert für das Querformat istALWAYS_ALLOW.setFinishPrimaryWithSecondary(): Legt fest, wie sich das Beenden aller Aktivitäten im sekundären Container auf die Aktivitäten im primären Container auswirkt.NEVERgibt an, dass das System die primären Aktivitäten nicht beenden soll, wenn alle Aktivitäten im sekundären Container abgeschlossen sind (siehe Aktivitäten beenden).setFinishSecondaryWithPrimary(): Legt fest, wie sich das Beenden aller Aktivitäten im primären Container auf die Aktivitäten im sekundären Container auswirkt.ALWAYSgibt 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 werden, wenn eine neue Aktivität im Container gestartet wird. Einfalse-Wert gibt an, dass neue Aktivitäten auf die Aktivitäten gestapelt werden, die sich bereits im sekundären Container befinden.
Rufen Sie die Singleton-Instanz von WindowManager
RuleControllerab und fügen Sie die Regel hinzu:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
Erstellen Sie einen Platzhalter für den sekundären Container, wenn keine Inhalte verfügbar sind:
Erstellen Sie ein
ActivityFilter, das die Aktivität identifiziert, mit der sich 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 );
Fügen Sie den Filter einem Filtersatz hinzu:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
SplitPlaceholderRuleerstellen: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(this, PlaceholderActivity.class) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build();
Mit
SplitPlaceholderRule.Builderwird die Regel erstellt und konfiguriert:placeholderActivityFilterSet: Enthält Aktivitätsfilter, die festlegen, wann die Regel angewendet werden soll. Dazu werden Aktivitäten identifiziert, denen die Platzhalteraktivität zugeordnet ist.Intent: Gibt den Start der Platzhalteraktivität an.setDefaultSplitAttributes(): Wendet Layoutattribute auf die Regel an.setMinWidthDp(): Legt die Mindestbreite des Displays (in dichteunabhängigen Pixeln, dp) fest, die für eine Aufteilung erforderlich ist.setMinSmallestWidthDp(): Legt den Mindestwert (in dp) fest, den die kleinere der beiden Displaydimensionen haben muss, damit ein Split unabhängig von der Ausrichtung des Geräts möglich ist.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.Dadurch werden Aktivitäten auf den meisten Tablets im Hochformat im Aufgabenfenster angezeigt. Siehe auchSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULTundsetMaxAspectRatioInLandscape(). Der Standardwert für das Querformat istALWAYS_ALLOW.setFinishPrimaryWithPlaceholder(): Legt fest, wie sich das Beenden 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 beendet wird (siehe Aktivitäten beenden).setSticky(): Gibt an, ob die Platzhalteraktivität auf kleinen Displays oben im Aktivitätsstapel angezeigt wird, nachdem der Platzhalter zuerst in einem Split mit ausreichender Mindestbreite angezeigt wurde.
Fügen Sie die Regel dem WindowManager
RuleControllerhinzu:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
Geben Sie Aktivitäten an, die niemals Teil einer Aufteilung sein sollten:
Erstellen Sie ein
ActivityFilter, das eine Aktivität identifiziert, die immer den gesamten Aufgabenanzeigebereich einnehmen sollte:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
Fügen Sie den Filter einem Filtersatz hinzu:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
ActivityRuleerstellen:Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
Mit
ActivityRule.Builderwird die Regel erstellt und konfiguriert:expandedActivityFilterSet: Enthält Aktivitätsfilter, die bestimmen, wann die Regel angewendet werden soll. Dazu werden Aktivitäten identifiziert, die Sie aus Segmenten ausschließen möchten.setAlwaysExpand(): Gibt an, ob die Aktivität das gesamte Aufgabenfenster ausfüllen soll.
Fügen Sie die Regel dem WindowManager
RuleControllerhinzu:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
App-übergreifende Einbettung
Unter Android 13 (API‑Level 33) und höher können Apps Aktivitäten aus anderen Apps einbetten. Durch das Einbetten von App-übergreifenden oder UID-übergreifenden Aktivitäten können Aktivitäten aus mehreren Android-Anwendungen visuell integriert werden. Das System zeigt eine Aktivität der Host-App und eine eingebettete Aktivität aus einer anderen App nebeneinander oder übereinander auf dem Bildschirm an, genau wie bei der Einbettung von Aktivitäten in einer einzelnen App.
Die Einstellungen-App könnte beispielsweise die Aktivität zur Auswahl des Hintergrundbilds aus der WallpaperPicker-App einbetten:
Vertrauensmodell
Hostprozesse, in die Aktivitäten aus anderen Apps eingebettet sind, 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 Angriffe zu starten, bei denen die Benutzeroberfläche manipuliert wird.
Um den Missbrauch der Einbettung von appübergreifenden Aktivitäten zu verhindern, müssen Apps unter Android die Einbettung ihrer Aktivitäten zulassen. Apps können Hosts als vertrauenswürdig oder nicht vertrauenswürdig kennzeichnen.
Vertrauenswürdige Hosts
Wenn Sie anderen Anwendungen erlauben möchten, die Darstellung von Aktivitäten aus Ihrer App einzubetten und vollständig zu steuern, geben Sie das SHA-256-Zertifikat der Hostanwendung 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, um mehrere Zertifikate anzugeben, ein Array von Strings:
<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-Zertifikats-Digest abrufen, indem sie die Gradle-Aufgabe signingReport ausführen. Der Zertifikats-Digest ist der SHA-256-Fingerabdruck ohne die Trennzeichen. Weitere Informationen finden Sie unter Signierungsbericht ausführen und Client authentifizieren.
Nicht vertrauenswürdige Hosts
Wenn Sie zulassen möchten, dass beliebige Apps die Aktivitäten Ihrer App einbetten und ihre Darstellung steuern, geben Sie das Attribut android:allowUntrustedActivityEmbedding im App-Manifest in den Elementen <activity> oder <application> an, z. B.:
<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
Um die Risiken der Einbettung nicht vertrauenswürdiger Aktivitäten zu minimieren, erstellen Sie einen benutzerdefinierten Authentifizierungsmechanismus, der die Hostidentität überprüft. Wenn Sie die Hostzertifikate kennen, verwenden Sie die androidx.security.app.authenticator-Bibliothek zur Authentifizierung. Wenn sich der Host nach dem Einbetten Ihrer Aktivität authentifiziert, können Sie die tatsächlichen Inhalte anzeigen. Wenn nicht, kannst du den Nutzer darüber informieren, dass die Aktion nicht zulässig war, und die Inhalte blockieren.
Verwenden Sie die Methode ActivityEmbeddingController#isActivityEmbedded() aus der Jetpack WindowManager-Bibliothek, um zu prüfen, ob ein Host Ihre Aktivität einbettet, z. B.:
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(context).isActivityEmbedded(activity); }
Einschränkung der Mindestgröße
Das Android-System wendet die im App-Manifestelement <layout> angegebene Mindesthöhe und ‑breite auf eingebettete Aktivitäten an. Wenn eine Anwendung keine Mindesthöhe und ‑breite angibt, gelten die Systemstandardwerte (sw220dp).
Wenn der Host versucht, die Größe des eingebetteten Containers auf eine Größe zu ändern, die kleiner als das Minimum ist, wird der eingebettete Container so erweitert, dass er die gesamten Taskgrenzen einnimmt.
<activity-alias>
Damit die Einbettung vertrauenswürdiger oder nicht vertrauenswürdiger 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, mit der die Sicherheit auf dem Systemserver überprüft wird, basiert auf den für das Ziel festgelegten Flags, nicht auf dem Alias.
Hostanwendung
Hostanwendungen implementieren das Einbetten von App-übergreifenden Aktivitäten auf dieselbe Weise wie das Einbetten von Aktivitäten in einer einzelnen App. SplitPairRule- und SplitPairFilter- oder ActivityRule- und ActivityFilter-Objekte geben eingebettete Aktivitäten und Aufgabenteilungen an. Aufteilungsregeln werden statisch in XML oder zur Laufzeit mit Jetpack WindowManager-API-Aufrufen definiert.
Wenn eine Hostanwendung versucht, eine Aktivität einzubetten, die nicht für die appübergreifende Einbettung aktiviert wurde, nimmt die Aktivität die gesamte Task-Grenze ein. Daher müssen Hostanwendungen wissen, ob Zielaktivitäten das Einbetten in andere Apps zulassen.
Wenn durch eine eingebettete Aktivität eine neue Aktivität in derselben Aufgabe gestartet wird und die neue Aktivität nicht für die appübergreifende Einbettung optimiert ist, nimmt die Aktivität die gesamte Aufgabe ein, anstatt die Aktivität im eingebetteten Container zu überlagern.
Eine Hostanwendung kann ihre eigenen Aktivitäten ohne Einschränkung einbetten, solange die Aktivitäten im selben Task gestartet werden.
Beispiele für Splits
Splitscreen-Modus aus dem Vollbildmodus starten
Es ist kein Refactoring erforderlich. Sie können die Konfiguration für die Aufteilung statisch oder zur Laufzeit definieren und dann Context#startActivity() ohne zusätzliche Parameter aufrufen.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Standardmäßig aufteilen
Wenn die Landingpage einer Anwendung für die Aufteilung in zwei Container auf großen Bildschirmen konzipiert ist, ist die Nutzerfreundlichkeit am besten, wenn beide Aktivitäten gleichzeitig erstellt und präsentiert werden. Inhalte sind jedoch möglicherweise erst für den sekundären Container des Splits verfügbar, wenn der Nutzer mit der Aktivität im primären Container interagiert (z. B. wenn er ein Element aus einem Navigationsmenü auswählt). Eine Platzhalteraktivität kann die Lücke füllen, bis Inhalte im sekundären Container des Splits angezeigt werden können (siehe Abschnitt Platzhalter).
Wenn Sie einen Split mit einem Platzhalter erstellen möchten, erstellen Sie einen Platzhalter und verknüpfen Sie ihn mit der primären Aktivität:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
Deeplink-Aufschlüsselung
Wenn eine App einen Intent empfängt, kann die Zielaktivität als sekundärer Teil einer Aktivitätsteilung 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.
Die Startanfrage sollte an die Hauptaktivität weitergeleitet werden und die Zielaktivität sollte in einem Split gestartet werden. Das System wählt automatisch die richtige Darstellung aus – gestapelt oder nebeneinander – basierend auf der verfügbaren Bildschirmbreite.
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) RuleController.getInstance(this) .addRule(SplitPairRule.Builder(filterSet).build()) startActivity(Intent(this, DetailActivity::class.java)) }
Java
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); RuleController.getInstance(this) .addRule(new SplitPairRule.Builder(filterSet).build()); startActivity(new Intent(this, DetailActivity.class)); }
Das Ziel des Deeplinks ist möglicherweise die einzige Aktivität, die dem Nutzer im Backstack zur Verfügung stehen sollte. Sie sollten vermeiden, die Detailaktivität zu schließen und nur die Hauptaktivität zu belassen:


Stattdessen können Sie beide Aktivitäten gleichzeitig beenden, indem Sie das Attribut finishPrimaryWithSecondary verwenden:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
Weitere Informationen finden Sie im Abschnitt Konfigurationsattribute.
Mehrere Aktivitäten in geteilten Containern
Durch das Stapeln mehrerer Aktivitäten in einem Split-Container können Nutzer auf detaillierte Inhalte zugreifen. Bei einer Liste mit Details muss der Nutzer möglicherweise einen untergeordneten Detailbereich aufrufen, während die primäre Aktivität beibehalten wird:
Kotlin
class DetailActivity : AppCompatActivity() { fun onOpenSubdetail() { startActivity(Intent(this, SubdetailActivity::class.java)) } }
Java
public class DetailActivity extends AppCompatActivity { void onOpenSubdetail() { startActivity(new Intent(this, SubdetailActivity.class)); } }
Die untergeordnete Detailaktivität wird über der Detailaktivität platziert und verdeckt sie:

Der Nutzer kann dann zur vorherigen Detailebene zurückkehren, indem er im Stapel zurücknavigiert:
Aktivitäten werden standardmäßig übereinander gestapelt, wenn sie über eine Aktivität im selben sekundären Container gestartet werden. Aktivitäten, die über den primären Container in einem aktiven Split gestartet werden, landen ebenfalls im sekundären Container oben im Aktivitätsstack.
Aktivitäten in einer neuen Aufgabe
Wenn durch Aktivitäten in einem geteilten Aufgabenfenster Aktivitäten in einer neuen Aufgabe gestartet werden, ist die neue Aufgabe von der Aufgabe mit der Aufteilung getrennt und wird im Vollbildmodus angezeigt. Auf dem Bildschirm „Letzte Apps“ werden zwei Aufgaben angezeigt: die Aufgabe im Split und die neue Aufgabe.
Aktivität ersetzen
Aktivitäten können im sekundären Container-Stack ersetzt werden, z. B. wenn die primäre Aktivität für die Navigation auf oberster Ebene und die sekundäre Aktivität für ein ausgewähltes Ziel verwendet wird. Bei jeder Auswahl in der Navigation der obersten Ebene sollte eine neue Aktivität im sekundären Container gestartet und die zuvor vorhandene(n) Aktivität(en) entfernt werden.
Wenn die App die Aktivität im sekundären Container nicht beendet, wenn sich die Navigationsauswahl ändert, kann die Rückwärtsnavigation verwirrend sein, wenn der Split-Screen minimiert wird (wenn das Gerät zusammengeklappt ist). Wenn Sie beispielsweise ein Menü im primären Bereich und die Bildschirme A und B im sekundären Bereich gestapelt haben, liegt beim Zusammenfalten des Smartphones B über A und A über dem Menü. Wenn der Nutzer von B zurückkehrt, wird A anstelle des Menüs angezeigt.
Bildschirm A muss in solchen Fällen aus dem Backstack entfernt werden.

Beim Starten auf der Seite in einem neuen Container über einem vorhandenen Split werden die neuen sekundären Container standardmäßig oben platziert und die alten im Backstack beibehalten. Sie können die Splits so konfigurieren, dass die vorherigen sekundären Container mit clearTop gelöscht werden, und neue Aktivitäten normal starten.
<SplitPairRule
window:clearTop="true">
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenA"/>
<SplitPairFilter
window:primaryActivityName=".Menu"
window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>
Kotlin
inner class MenuActivity : AppCompatActivity() { fun onMenuItemSelected(selectedMenuItem: Int) { startActivity(Intent(this, classForItem(selectedMenuItem))) } }
Java
public class MenuActivity extends AppCompatActivity{ void onMenuItemSelected(int selectedMenuItem) { startActivity(new Intent(this, classForItem(selectedMenuItem))); } }
Alternativ können Sie dieselbe sekundäre Aktivität verwenden und aus der primären Aktivität (Menü) neue Intents senden, die in derselben Instanz aufgelöst werden, aber eine Status- oder UI-Aktualisierung im sekundären Container auslösen.
Mehrere Splits
Apps können eine mehrstufige Navigation ermöglichen, indem sie zusätzliche Aktivitäten auf der Seite starten.
Wenn durch eine Aktivität in einem sekundären Container eine neue Aktivität seitlich gestartet wird, wird über der vorhandenen Aufteilung eine neue Aufteilung erstellt.
Der Backstack enthält alle Aktivitäten, die zuvor geöffnet wurden. Nutzer können also nach Abschluss von C zur A/B-Aufteilung zurückkehren.

Wenn Sie einen neuen Split erstellen möchten, starten Sie die neue Aktivität seitlich vom vorhandenen sekundären Container. Konfigurieren Sie die Splits für A/B und B/C und starten Sie Aktivität C normal von B aus:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
<SplitPairFilter
window:primaryActivityName=".B"
window:secondaryActivityName=".C"/>
</SplitPairRule>
Kotlin
class B : AppCompatActivity() { fun onOpenC() { startActivity(Intent(this, C::class.java)) } }
Java
public class B extends AppCompatActivity{ 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.
Wenn zwei Aktivitäten, die ein gemeinsames UI-Element haben, in einem Split enthalten sind, ist es redundant und möglicherweise verwirrend, das Element in beiden Aktivitäten anzuzeigen.
Wenn Sie wissen möchten, wann Aktivitäten in einem Split sind, prüfen Sie den SplitController.splitInfoList-Ablauf oder registrieren Sie einen Listener mit SplitControllerCallbackAdapter für Änderungen am Split-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 { 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) { super.onCreate(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, werden aber in der Regel im Status STARTED gestartet, um Ressourcen zu schonen. Weitere Informationen finden Sie unter Kotlin-Coroutinen mit lebenszyklusbewussten Komponenten verwenden.
Callbacks können in jedem Lebenszyklusstatus erfolgen, auch wenn eine Aktivität beendet wird. Listener sollten in der Regel in onStart() registriert und in onStop() nicht registriert sein.
Modales Fenster im Vollbildmodus
Bei einigen Aktivitäten können Nutzer erst mit der App interagieren, wenn eine bestimmte Aktion ausgeführt wurde, z. B. bei einem Anmeldebildschirm, einem Bildschirm zur Bestätigung von Richtlinien oder einer Fehlermeldung. Modale Aktivitäten sollten nicht in einem Split angezeigt werden.
Eine Aktivität kann so konfiguriert werden, dass sie das Aufgabenfenster immer ausfüllt:
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Aktivitäten abschließen
Nutzer können Aktivitäten auf beiden Seiten des Splits beenden, indem sie vom Displayrand wischen:
Wenn das Gerät so eingerichtet ist, dass die Zurück-Taste anstelle der Gestennavigation verwendet wird, wird die Eingabe an die fokussierte Aktivität gesendet – die Aktivität, die zuletzt berührt oder gestartet wurde.
Die Auswirkungen, die das Abschließen aller Aktivitäten in einem Container auf den gegenüberliegenden Container hat, hängen von der Split-Konfiguration ab.
Konfigurationsattribute
Sie können Attribute für Split-Paar-Regeln angeben, um zu konfigurieren, wie sich das Beenden aller Aktivitäten auf einer Seite des Splits auf die Aktivitäten auf der anderen Seite des Splits auswirkt. Folgende Attribute sind verfügbar:
window:finishPrimaryWithSecondary– Auswirkungen des Abschlusses aller Aktivitäten im sekundären Container auf die Aktivitäten im primären Containerwindow:finishSecondaryWithPrimary: Auswirkungen des Abschlusses aller Aktivitäten im primären Container auf die Aktivitäten im sekundären Container
Mögliche Werte der Attribute:
always: Aktivitäten immer im zugehörigen Container abschließennever: Die Aktivitäten im zugehörigen Container werden nie abgeschlossen.adjacent: Schließen Sie die Aktivitäten im zugehörigen Container ab, wenn die beiden Container nebeneinander angezeigt werden, nicht aber, wenn sie gestapelt sind.
Beispiel:
<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 Splits abgeschlossen sind, nimmt der verbleibende Container das gesamte Fenster ein:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>


Aktivitäten gemeinsam abschließen
Aktivitäten im primären Container automatisch abschließen, wenn alle Aktivitäten im sekundären Container abgeschlossen sind:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>


Aktivitäten im sekundären Container automatisch beenden, wenn alle Aktivitäten im primären Container abgeschlossen sind:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>


Aktivitäten gemeinsam abschließen, wenn alle Aktivitäten im primären oder sekundären Container abgeschlossen sind:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>


Mehrere Aktivitäten in Containern abschließen
Wenn mehrere Aktivitäten in einem geteilten Container gestapelt sind, werden durch das Beenden einer Aktivität unten im Stapel nicht automatisch die Aktivitäten oben beendet.
Wenn sich beispielsweise zwei Aktivitäten im sekundären Container befinden, wobei C über B liegt:

Die Konfiguration der Aufteilung wird durch die Konfiguration der Aktivitäten A und B definiert:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Wenn Sie die obere Aktivität beenden, bleibt die Aufteilung erhalten.

Wenn die unterste (Stamm-)Aktivität des sekundären Containers beendet wird, werden die darüber liegenden Aktivitäten nicht entfernt und die Aufteilung bleibt erhalten.

Alle zusätzlichen Regeln für das gemeinsame Beenden von Aktivitäten, z. B. das Beenden der sekundären Aktivität mit der primären, werden ebenfalls ausgeführt:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>

Wenn der Split so konfiguriert ist, dass primäre und sekundäre Varianten gleichzeitig enden:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>



Aufteilungseigenschaften zur Laufzeit ändern
Die Eigenschaften eines aktiven und sichtbaren Splits können nicht geändert werden. Wenn Sie die Aufteilungsregeln ändern, wirkt sich das auf zusätzliche Aktivitätsstarts und neue Container aus, nicht aber auf bestehende und aktive Aufteilungen.
Wenn Sie die Eigenschaften aktiver Splits ändern möchten, beenden Sie die Nebenaktivität oder ‑aktivitäten im Split und starten Sie sie mit einer neuen Konfiguration noch einmal.
Dynamische Aufteilungseigenschaften
Android 15 (API-Level 35) und höher, unterstützt von Jetpack WindowManager 1.4 und höher, bieten dynamische Funktionen, die die Konfiguration von Splits für das Einbetten von Aktivitäten ermöglichen, darunter:
- Bereichserweiterung:Mit einer interaktiven, ziehbaren Trennlinie können Nutzer die Größe der Bereiche in einer geteilten Präsentation anpassen.
- Anpinnen des Aktivitätsstacks:Nutzer können die Inhalte in einem Container anpinnen und die Navigation im Container von der Navigation im anderen Container isolieren.
- Dialog – Vollbild dimmen:Wenn ein Dialogfeld angezeigt wird, können Apps angeben, ob das gesamte Aufgabenfenster oder nur der Container, in dem das Dialogfeld geöffnet wurde, gedimmt werden soll.
Bereich maximieren
Durch das Aufziehen von Bereichen können Nutzer den Bildschirmbereich anpassen, der den beiden Aktivitäten in einem Layout mit zwei Bereichen zugewiesen ist.
So passen Sie die Darstellung des Fensterteilers an und legen den ziehbaren Bereich des Teilers fest:
Instanz von
DividerAttributeserstellenTrennlinienattribute anpassen:
color:Die Farbe des ziehbaren Bereichstrennzeichens.widthDp:Die Breite des ziehbaren Bereichstrennzeichens. Legen SieWIDTH_SYSTEM_DEFAULTfest, damit das System die Breite des Trennzeichens bestimmt.Bereich ziehen:Der Mindestprozentsatz des Bildschirms, den die einzelnen Bereiche einnehmen können. Kann zwischen 0,33 und 0,66 liegen. Legen Sie
DRAG_RANGE_SYSTEM_DEFAULTfest, damit das System den Drag-Bereich bestimmt.
Kotlin
val splitAttributesBuilder: SplitAttributes.Builder = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) if (WindowSdkExtensions.getInstance().extensionVersion >= 6) { splitAttributesBuilder.setDividerAttributes( DividerAttributes.DraggableDividerAttributes.Builder() .setColor(getColor(R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ) } val splitAttributes: SplitAttributes = splitAttributesBuilder.build()
Java
SplitAttributes.Builder splitAttributesBuilder = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.33f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT); if (WindowSdkExtensions.getInstance().getExtensionVersion() >= 6) { splitAttributesBuilder.setDividerAttributes( new DividerAttributes.DraggableDividerAttributes.Builder() .setColor(ContextCompat.getColor(this, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ); } SplitAttributes _splitAttributes = splitAttributesBuilder.build();
Aktivitätspaket anpinnen
Durch das Anpinnen von Aktivitätsstapeln können Nutzer eines der geteilten Fenster anpinnen, sodass die Aktivität unverändert bleibt, während sie im anderen Fenster navigieren. Das Anpinnen von Aktivitätsstacks bietet eine verbesserte Multitasking-Funktion.
So aktivieren Sie das Anpinnen von Aktivitätsstacks in Ihrer App:
Fügen Sie der Layoutdatei der Aktivität, die Sie anpinnen möchten, eine Schaltfläche hinzu, z. B. der Detailaktivität eines Listen-/Detail-Layouts:
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/detailActivity" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white" tools:context=".DetailActivity"> <TextView android:id="@+id/textViewItemDetail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="36sp" android:textColor="@color/obsidian" app:layout_constraintBottom_toTopOf="@id/pinButton" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/pinButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/pin_this_activity" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/textViewItemDetail"/> </androidx.constraintlayout.widget.ConstraintLayout>Legen Sie in der Methode
onCreate()der Aktivität einen OnClickListener für die Schaltfläche fest:Kotlin
val pinButton: Button = findViewById(R.id.pinButton) pinButton.setOnClickListener { val splitAttributes: SplitAttributes = SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build() val pinSplitRule = SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build() SplitController.getInstance(applicationContext) .pinTopActivityStack(taskId, pinSplitRule) }
Java
Button pinButton = findViewById(R.id.pinButton); pinButton.setOnClickListener( (view) -> { SplitAttributes splitAttributes = new SplitAttributes.Builder() .setSplitType(SplitAttributes.SplitType.ratio(0.66f)) .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT) .build(); SplitPinRule pinSplitRule = new SplitPinRule.Builder() .setSticky(true) .setDefaultSplitAttributes(splitAttributes) .build(); SplitController.getInstance(getApplicationContext()) .pinTopActivityStack(getTaskId(), pinSplitRule); });
Dialogfeld im Vollbildmodus – Abdunklung
Bei Aktivitäten wird das Display in der Regel abgedunkelt, um die Aufmerksamkeit auf einen Dialog zu lenken. Bei der Einbettung von Aktivitäten sollten beide Bereiche der Dual-Pane-Anzeige abgedunkelt werden, nicht nur der Bereich mit der Aktivität, die das Dialogfeld geöffnet hat. So wird eine einheitliche Benutzeroberfläche geschaffen.
In WindowManager 1.4 und höher wird das gesamte App-Fenster standardmäßig abgedunkelt, wenn ein Dialogfeld geöffnet wird (siehe EmbeddingConfiguration.DimAreaBehavior.ON_TASK).
Wenn Sie nur den Container der Aktivität dimmen möchten, mit der das Dialogfeld geöffnet wurde, verwenden Sie EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK.
Aktivität aus einem geteilten Fenster in ein Vollbildfenster extrahieren
Erstellen Sie eine neue Konfiguration, in der die Seitenaktivität im Vollbildmodus angezeigt wird, und starten Sie die Aktivität dann mit einem Intent neu, der in derselben Instanz aufgelöst wird.
Unterstützung für Split-Screen-Modus zur Laufzeit 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 früheren Plattformversionen verfügbar. Um zur Laufzeit die Verfügbarkeit des Features zu prüfen, verwenden Sie das Attribut SplitController.splitSupportStatus oder die Methode 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. }
Wenn Splits nicht unterstützt werden, werden Aktivitäten oben im Aktivitätsstack gestartet (gemäß dem Modell ohne Aktivitätseinbettung).
System Override verhindern
Die Hersteller von Android-Geräten (Erstausrüster oder OEMs) können die Einbettung von Aktivitäten als Funktion des Gerätesystems implementieren. Das System gibt Aufteilungsregeln für Apps mit mehreren Aktivitäten an und überschreibt damit das Windowing-Verhalten der Apps. Durch die Systemüberschreibung wird für Apps mit mehreren Aktivitäten ein vom System definierter Modus für die Einbettung von Aktivitäten erzwungen.
Durch die Einbettung von Systemaktivitäten kann die Darstellung von Apps durch Layouts mit mehreren Bereichen wie Liste/Detail verbessert werden, ohne dass Änderungen an der App erforderlich sind. Die Einbettung von Systemaktivitäten kann jedoch auch zu falschen App-Layouts, Fehlern oder Konflikten mit der von der App implementierten Einbettung von Aktivitäten führen.
Ihre App kann das Einbetten von Systemaktivitäten verhindern oder zulassen, indem Sie PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE in der Manifestdatei der App festlegen, z. B.:
<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 Property-Name wird im Jetpack WindowManager-Objekt WindowProperties definiert. Setzen Sie den Wert auf false, wenn Ihre App Activity Embedding implementiert oder Sie verhindern möchten, dass das System seine Activity Embedding-Regeln auf Ihre App anwendet. Setzen Sie den Wert auf true, damit das System systemdefinierte Activity Embeddings auf Ihre App anwenden kann.
Einschränkungen, Beschränkungen und Vorbehalte
- Nur die Host-App der Aufgabe, die als Eigentümer der Root-Aktivität in der Aufgabe identifiziert wird, kann andere Aktivitäten in der Aufgabe organisieren und einbetten. Wenn Aktivitäten, die das Einbetten und Aufteilen unterstützen, in einer Aufgabe ausgeführt werden, die zu einer anderen Anwendung gehört, funktionieren das Einbetten und Aufteilen für diese Aktivitäten nicht.
- Aktivitäten können nur in einer einzelnen Aufgabe organisiert werden. Wenn Sie eine Aktivität in einem neuen Task starten, wird sie immer in einem neuen maximierten Fenster außerhalb vorhandener Splits angezeigt.
- Nur Aktivitäten im selben Prozess können organisiert und in einen Split eingefügt werden. Der
SplitInfo-Callback meldet nur Aktivitäten, die zum selben Prozess gehören, da Aktivitäten in anderen Prozessen nicht bekannt sind. - Jede Paar- oder Einzelaktivitätsregel gilt nur für Aktivitätsstarts, die nach der Registrierung der Regel erfolgen. Derzeit ist es nicht möglich, vorhandene Splits oder ihre visuellen Eigenschaften zu aktualisieren.
- Die Filterkonfiguration für das geteilte Paar muss mit den Intents übereinstimmen, die beim vollständigen Starten von Aktivitäten verwendet werden. Der Abgleich erfolgt zu dem Zeitpunkt, zu dem eine neue Aktivität über den Anwendungsprozess gestartet wird. Daher sind möglicherweise keine Komponentennamen bekannt, die später im Systemprozess aufgelöst werden, wenn implizite Intents verwendet werden. Wenn ein Komponentenname zum Zeitpunkt des Starts nicht bekannt ist, kann stattdessen ein Platzhalter („*/*“) verwendet und nach Intent-Aktion gefiltert werden.
- Derzeit ist es nicht möglich, Aktivitäten zwischen Containern oder in und aus Splits zu verschieben, nachdem sie erstellt wurden. Splits werden von der WindowManager-Bibliothek nur erstellt, wenn neue Aktivitäten mit übereinstimmenden Regeln gestartet werden. Sie werden zerstört, wenn die letzte Aktivität in einem Split-Container beendet wird.
- Aktivitäten können neu gestartet werden, wenn sich die Konfiguration ändert. Wenn also ein Split erstellt oder entfernt wird und sich die Aktivitätsgrenzen ändern, kann die Aktivität vollständig zerstört und neu erstellt werden. App-Entwickler sollten daher vorsichtig sein, wenn sie beispielsweise neue Aktivitäten über Lebenszyklus-Callbacks starten.
- Geräte müssen die Window Extensions-Schnittstelle enthalten, um das Einbetten von Aktivitäten zu unterstützen. Die Schnittstelle ist auf fast allen Geräten mit großem Display verfügbar, auf denen Android 12L (API-Level 32) oder höher ausgeführt wird. Einige Geräte mit großem Display, auf denen keine mehreren Aktivitäten gleichzeitig ausgeführt werden können, enthalten jedoch nicht die Fenstererweiterungsschnittstelle. Wenn ein Gerät mit großem Display den Multi-Window-Modus nicht unterstützt, wird die Einbettung von Aktivitäten möglicherweise auch nicht unterstützt.
Zusätzliche Ressourcen
- Codelabs:
- Lernpfad: Aktivitäten einbetten
- Beispiel-App: activity-embedding