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.
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:
Oder eine Aktivität, die das gesamte Aufgabenfenster einnimmt, kann eine Aufteilung erstellen, indem eine neue Aktivität zusammen mit folgenden gestartet wird:
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:
Verschieben Sie die Teilung zur Seite und verschieben Sie sie zur Seite, um die vorherige primäre Aktivität zu verbergen:
Starten Sie eine Aktivität oben, d. h. im selben Aktivitätsstapel:
Öffnen Sie in derselben Aufgabe ein vollständiges Aktivitätsfenster:
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.
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 rechtsrtl
: Von rechts nach linkslocale
: Entwederltr
oderrtl
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:
setSplitType()
: Legt die Anteile der aufgeteilten Container fest. Gültige Argumente, einschließlich der MethodeSplitAttributes.SplitType.ratio()
, finden Sie unterSplitAttributes.SplitType
.setLayoutDirection()
: Legt das Layout der Container fest. Mögliche Werte finden Sie unterSplitAttributes.LayoutDirection
.
Beispiele finden Sie unten im Abschnitt WindowManager API.
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.
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).
Ä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:
zur folgenden Z-Reihenfolge von Aktivitäten in derselben Aufgabe führt:
In einem kleineren Aufgabenfenster wird die Anwendung also auf eine einzelne Aktivität verkleinert, wobei sich C oben im Stack befindet:
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:
Das Ergebnis ist die folgende Z-Reihenfolge von Aktivitäten in derselben Aufgabe:
In einem kleineren Aufgabenfenster verkleinert sich die Anwendung auf eine einzelne Aktivität mit C:
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.
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.
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:
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.
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:
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>
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-BibliothekInitializer
von Jetpack stelltRuleController
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:
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'
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 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üge deiner App-Manifestdatei
androidx.startup.InitializationProvider
als<provider>
hinzu. Fügen Sie einen Verweis auf die Implementierung desRuleController
-InitialisierersSplitInitializer
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 initialisiertSplitInitializer
, bevor die MethodeonCreate()
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:
Erstellen Sie eine Aufteilungsregel:
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 );
Fügen Sie den Filter einer Filtergruppe hinzu:
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
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.
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 unterSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
undsetMaxAspectRatioInLandscape
. Der Standardwert für das Querformat istALWAYS_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.
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);
Erstellen Sie einen Platzhalter für den sekundären Container, wenn kein Inhalt verfügbar ist:
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 );
Fügen Sie den Filter einer Filtergruppe hinzu:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
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 unterSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
undsetMaxAspectRatioInLandscape
. Der Standardwert für das Querformat istALWAYS_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.
Fügen Sie die Regel zu WindowManager
RuleController
hinzu:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
Geben Sie Aktivitäten an, die nie Teil einer Aufteilung sein sollen:
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 );
Fügen Sie den Filter einer Filtergruppe hinzu:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
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.
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:
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
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).
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>
Aufteilung nach Deeplink
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.
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:
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:
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:
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.
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.
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.
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.
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.
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.
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:
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>
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>
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>
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>
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:
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.
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.
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>
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>
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
- Codelab – Ein Listendetaillayout mit Aktivitäteneinbettungen und Material Design erstellen
- Lernpfad – Eingebettete Aktivitäten