L'inserimento di attività ottimizza le app sui dispositivi con schermi di grandi dimensioni dividendo la finestra delle attività di un'applicazione tra due attività o due istanze della stessa attività.
Se la tua app è composta da più attività, l'inserimento delle attività ti consente di offrire un'esperienza utente migliorata su tablet, dispositivi pieghevoli e ChromeOS.
L'inserimento di attività non richiede il refactoring del codice. Puoi determinare in che modo la tua app visualizza le attività, affiancate o impilate, creando un file di configurazione XML o effettuando chiamate all'API Jetpack WindowManager.
Il supporto per gli schermi di piccole dimensioni viene mantenuto automaticamente. Quando l'app è su un dispositivo con uno schermo piccolo, le attività vengono impilate una sopra l'altra. Sugli schermi di grandi dimensioni, le attività vengono visualizzate una accanto all'altra. Il sistema determina la presentazione in base alla configurazione che hai creato, senza bisogno di logica di ramificazione.
L'inserimento di attività è compatibile con le modifiche dell'orientamento del dispositivo e funziona perfettamente su dispositivi pieghevoli, impilando e scompilando le attività man mano che il dispositivo si apre e si chiude.
L'inserimento di attività è supportato sulla maggior parte dei dispositivi con schermo di grandi dimensioni con Android 12L (livello API 32) e versioni successive.
Finestra dell'attività divisa
L'inserimento di attività suddivide la finestra delle attività dell'app in due contenitori: principale e secondario. I contenitori contengono le attività lanciate dall'attività principale o da altre attività già presenti nei contenitori.
Le attività vengono impilate nel contenitore secondario man mano che vengono lanciate e il contenitore secondario viene sovrapposto al contenitore principale su schermi di piccole dimensioni, pertanto l'impilamento delle attività e la navigazione a ritroso sono in linea con l'ordine delle attività già integrate nella tua app.
L'inserimento delle attività ti consente di visualizzarle in diversi modi. La tua app può suddividere la finestra delle attività lanciando contemporaneamente due attività affiancate:
In alternativa, un'attività che occupa l'intera finestra delle attività può creare una suddivisione avviando una nuova attività accanto:
Le attività già in una visualizzazione divisa e che condividono una finestra delle attività possono avviare altre attività nei seguenti modi:
A lato sopra un'altra attività:
A lato e sposta la suddivisione lateralmente, nascondendo l'attività principale precedente:
Avvia un'attività in primo piano, ovvero nello stesso stack di attività:
Avvia una finestra completa dell'attività nella stessa attività:
Navigazione a ritroso
Tipi diversi di applicazioni possono avere regole di navigazione a ritroso diverse in uno stato della finestra dell'attività divisa a seconda delle dipendenze tra le attività o del modo in cui gli utenti attivano l'evento Indietro, ad esempio:
- In coppia: se le attività sono correlate e una non deve essere mostrata senza l'altra, la navigazione a ritroso può essere configurata per completare entrambe.
- Attività indipendenti: se le attività sono completamente indipendenti, il ritorno all'attività precedente non influisce sullo stato di un'altra attività nella finestra delle attività.
L'evento indietro viene inviato all'ultima attività attivata quando si utilizza la navigazione con i pulsanti.
Per la navigazione basata sui gesti:
Android 14 (livello API 34) e versioni precedenti: l'evento Indietro viene inviato all'attività in cui si è verificato il gesto. Quando gli utenti scorrono dal lato sinistro schiermo, l'evento Indietro viene inviato all'attività nel riquadro sinistra della finestra divisa. Quando gli utenti scorrono dal lato destro dello schermo, l'evento Indietro viene inviato all'attività nel riquadro a destra.
Android 15 (livello API 35) e versioni successive
Quando si gestiscono più attività della stessa app, il gesto completa l'attività in primo piano indipendentemente dalla direzione dello scorrimento, offrendo un'esperienza più unificata.
Negli scenari che coinvolgono due attività di app diverse (sovrapposizione), l'evento indietro viene indirizzato all'ultima attività attiva, in linea con il comportamento della navigazione con i pulsanti.
Layout a più riquadri
Jetpack WindowManager ti consente di creare un'attività che incorpora un layout con più riquadri su dispositivi con schermi grandi con Android 12L (livello API 32) o versioni successive e su alcuni dispositivi con versioni precedenti della piattaforma. Le app esistenti basate su più attività anziché su frammenti o layout basati su visualizzazioni, come SlidingPaneLayout
, possono offrire un'esperienza utente migliorata su schermi di grandi dimensioni senza eseguire il refactoring del codice sorgente.
Un esempio comune è la suddivisione in elenco e dettagli. Per garantire una presentazione di alta qualità, il sistema avvia l'attività di elenco, dopodiché l'applicazione avvia immediatamente l'attività dei dettagli. Il sistema di transizione attende fino a quando entrambe le attività non vengono disegnate, quindi le mostra insieme. Per l'utente, le due attività vengono avviate come una sola.
Attributi suddivisi
Puoi specificare la proporzione della finestra delle attività tra i contenitori suddivisi e la disposizione dei contenitori rispetto l'uno all'altro.
Per le regole definite in un file di configurazione XML, imposta i seguenti attributi:
splitRatio
: imposta le proporzioni del contenitore. Il valore è un numero in virgola mobile nell'intervallo aperto (0,0, 1,0).splitLayoutDirection
: specifica la disposizione dei container suddivisi rispetto a un altro. I valori includono:ltr
: da sinistra a destrartl
: da destra a sinistralocale
:ltr
ortl
viene determinato in base all'impostazione internazionale
Per esempi, consulta la sezione Configurazione XML.
Per le regole create utilizzando le API WindowManager, crea un oggetto SplitAttributes
con SplitAttributes.Builder
e chiama i seguenti metodi di compilatore:
setSplitType()
: imposta le proporzioni dei contenitori suddivisi. ConsultaSplitAttributes.SplitType
per gli argomenti validi, incluso il metodoSplitAttributes.SplitType.ratio()
.setLayoutDirection()
: imposta il layout dei contenitori. VediSplitAttributes.LayoutDirection
per i valori possibili.
Per esempi, consulta la sezione API WindowManager.
Segnaposto
Le attività segnaposto sono attività secondarie vuote che occupano un'area di una suddivisione delle attività. Alla fine, devono essere sostituite da un'altra attività che contenga contenuti. Ad esempio, un'attività segnaposto potrebbe occupare il lato secondario di un'attività divisa in un layout di elenco dettagliato finché non viene selezionato un elemento dell'elenco, a quel punto un'attività contenente le informazioni dettagliate per l'elemento dell'elenco selezionato sostituisce il segnaposto.
Per impostazione predefinita, il sistema mostra i segnaposto solo quando è presente spazio sufficiente per una suddivisione delle attività. I segnaposto vengono completati automaticamente quando le dimensioni del display diventano una larghezza o un'altezza troppo piccole per visualizzare una suddivisione. Quando lo spazio lo consente, il sistema riavvia il segnaposto con uno stato reinizializzato.
Tuttavia, l'attributo stickyPlaceholder
di un metodo SplitPlaceholderRule
o
setSticky()
di SplitPlaceholder.Builder
può sostituire il
comportamento predefinito. Quando l'attributo o il metodo specifica un valore true
, il sistema mostra il segnaposto come attività principale nella finestra delle attività quando la visualizzazione viene ridotta a un riquadro da una visualizzazione a due riquadri (per un esempio, consulta Configurazione divisa).
Modifiche alle dimensioni della finestra
Quando le modifiche alla configurazione del dispositivo riducono la larghezza della finestra delle attività in modo che non sia sufficientemente grande per un layout a più riquadri (ad esempio, quando un dispositivo pieghevole con schermo grande si piega dalle dimensioni di un tablet a quelle di uno smartphone o la finestra dell'app viene ridimensionata in modalità multi-finestra), le attività non segnaposto nel riquadro secondario della finestra delle attività vengono impilate sopra le attività nel riquadro principale.
Le attività segnaposto vengono mostrate solo quando la larghezza di visualizzazione è sufficiente per una suddivisione. Su schermi più piccoli, il segnaposto viene ignorato automaticamente. Quando l'area di visualizzazione diventa di nuovo sufficientemente grande, il segnaposto viene ricreato. (vedi la sezione Segnaposto).
La sovrapposizione delle attività è possibile perché WindowManager ordina le attività nel riquadro secondario sopra le attività nel riquadro principale.
Più attività nel riquadro secondario
L'attività B avvia l'attività C in situ senza flag di intent aggiuntivi:
Il risultato è il seguente ordine Z delle attività nella stessa attività:
Di conseguenza, in una finestra delle attività più piccola, l'applicazione si riduce a una singola attività con C nella parte superiore dello stack:
Se torni indietro nella finestra più piccola, puoi spostarti tra le attività impilate una sopra l'altra.
Se la configurazione della finestra dell'attività viene ripristinata a dimensioni maggiori che possono ospitare più riquadri, le attività vengono visualizzate di nuovo una accanto all'altra.
Suddivisioni in pila
L'attività B avvia l'attività C di lato e sposta la suddivisione lateralmente:
Il risultato è il seguente ordine Z delle attività nella stessa attività:
In una finestra delle attività più piccola, l'applicazione si riduce a una singola attività con C in cima:
Orientamento verticale fisso
L'impostazione manifest android:screenOrientation consente alle app di limitare le attività all'orientamento verticale o orizzontale. Per migliorare l'esperienza utente su dispositivi con schermi di grandi dimensioni, come tablet e dispositivi pieghevoli, i produttori di dispositivi (OEM) possono ignorare le richieste di orientamento dello schermo e applicare la modalità letterbox all'app in orientamento verticale su display orizzontali o in orientamento orizzontale su display verticali.
Analogamente, quando l'inserimento di attività è attivo, gli OEM possono personalizzare i dispositivi per visualizzare le attività in formato letterbox con orientamento verticale fisso in orizzontale su schermi di grandi dimensioni (larghezza ≥ 600 dp). Quando un'attività in modalità Ritratto fissa avvia una seconda attività, il dispositivo può visualizzare le due attività una accanto all'altra in una visualizzazione a due riquadri.
Aggiungi sempre la proprietà android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
al file manifest dell'app per informare i dispositivi che la tua app supporta l'embedding delle attività (vedi la sezione Configurazione della suddivisione). I dispositivi personalizzati dall'OEM possono quindi determinare se applicare il letterbox alle attività in formato Ritratto fisso.
Configurazione della suddivisione
Le regole di suddivisione configurano le suddivisioni delle attività. Puoi definire le regole di suddivisione in un file di configurazione XML o tramite chiamate all'API WindowManager di Jetpack.
In entrambi i casi, l'app deve accedere alla libreria WindowManager e deve informare il sistema che ha implementato l'inserimento di attività.
Procedi nel seguente modo:
Aggiungi la dipendenza della libreria WindowManager più recente al file
build.gradle
a livello di modulo dell'app, ad esempio:implementation 'androidx.window:window:1.1.0-beta02'
La libreria WindowManager fornisce tutti i componenti necessari per l'embedding delle attività.
Comunica al sistema che la tua app ha implementato l'inserimento di attività.
Aggiungi la proprietà
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
all'elemento <application> del file manifest dell'app e imposta il valore su true, ad esempio:<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>
Nella versione 1.1.0-alpha06 e successive di WindowManager, le suddivisioni dell'inserimento di attività sono disattivate, a meno che la proprietà non venga aggiunta al manifest e impostata su true.
Inoltre, i produttori di dispositivi utilizzano l'impostazione per attivare funzionalità personalizzate per le app che supportano l'inserimento di attività. Ad esempio, i dispositivi possono applicare il letterbox a un'attività solo in formato verticale su schermi orizzontali per orientarla in vista della transizione a un layout a due riquadri all'avvio di una seconda attività (vedi Orientamento verticale fisso).
Configurazione XML
Per creare un'implementazione basata su XML dell'inserimento di attività, completa i seguenti passaggi:
Crea un file di risorse XML che:
- Definisce le attività che condividono una suddivisione
- Configura le opzioni di suddivisione
- Crea un segnaposto per il contenitore secondario della suddivisione quando i contenuti non sono disponibili
- Specifica le attività che non devono mai far parte di una suddivisione
Ad esempio:
<!-- 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>
Crea un'inizializzazione.
Il componente WindowManager
RuleController
analizza il file di configurazione XML e rende disponibili le regole per il sistema. Una libreria Startup di JetpackInitializer
rende disponibile il file XML perRuleController
all'avvio dell'app in modo che le regole siano in vigore all'avvio di qualsiasi attività.Per creare un'inizializzazione:
Aggiungi la dipendenza della libreria Jetpack Startup più recente al file
build.gradle
a livello di modulo, ad esempio:implementation 'androidx.startup:startup-runtime:1.1.1'
Crea una classe che implementi l'interfaccia
Initializer
.L'inizializzatore rende disponibili le regole di suddivisione per
RuleController
passando l'ID del file di configurazione XML (main_split_config.xml
) al metodoRuleController.parseRules()
.Kotlin
class SplitInitializer : Initializer<RuleController> { override fun create(context: Context): RuleController { return RuleController.getInstance(context).apply { setRules(RuleController.parseRules(context, R.xml.main_split_config)) } } override fun dependencies(): List<Class<out Initializer<*>>> { return emptyList() } }
Java
public class SplitInitializer implements Initializer<RuleController> { @NonNull @Override public RuleController create(@NonNull Context context) { RuleController ruleController = RuleController.getInstance(context); ruleController.setRules( RuleController.parseRules(context, R.xml.main_split_config) ); return ruleController; } @NonNull @Override public List<Class<? extends Initializer<?>>> dependencies() { return Collections.emptyList(); } }
Crea un fornitore di contenuti per le definizioni delle regole.
Aggiungi
androidx.startup.InitializationProvider
al file manifest dell'app come<provider>
. Includi un riferimento all'implementazione dell'inizializzatoreRuleController
,SplitInitializer
:<!-- AndroidManifest.xml --> <provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <!-- Make SplitInitializer discoverable by InitializationProvider. --> <meta-data android:name="${applicationId}.SplitInitializer" android:value="androidx.startup" /> </provider>
InitializationProvider
rileva e inizializzaSplitInitializer
prima della chiamata al metodoonCreate()
dell'app. Di conseguenza, le regole di suddivisione vengono applicate all'avvio dell'attività principale dell'app.
API WindowManager
Puoi implementare l'inserimento di attività in modo programmatico con alcune chiamate API. Esegui le chiamate nel metodo onCreate()
di una sottoclasse di
Application
per assicurarti che le regole siano in vigore prima del lancio di qualsiasi attività.
Per creare un'attività suddivisa in modo programmatico:
Crea una regola di suddivisione:
Crea un
SplitPairFilter
che identifichi le attività che condividono la suddivisione: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 );
Aggiungi il filtro a un insieme di filtri:
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
Crea gli attributi del layout per la suddivisione:
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
crea un oggetto contenente gli attributi di layout:setSplitType()
: definisce in che modo l'area di visualizzazione disponibile viene assegnata a ciascun contenitore di attività. Il tipo di suddivisione in base al rapporto specifica la proporzione dell'area di visualizzazione disponibile allocata al contenitore principale. Il contenitore secondario occupa la parte rimanente dell'area di visualizzazione disponibile.setLayoutDirection()
: specifica la disposizione dei contenitori delle attività rispetto l'uno all'altro, prima il contenitore principale.
Crea un
SplitPairRule
:Kotlin
val splitPairRule = SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build()
Java
SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet) .setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER) .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS) .setClearTop(false) .build();
SplitPairRule.Builder
crea e configura la regola:filterSet
: contiene filtri di coppie di suddivisioni che determinano quando applicare la regola identificando le attività che condividono una suddivisione.setDefaultSplitAttributes()
: applica gli attributi di layout alla regola.setMinWidthDp()
: imposta la larghezza minima del display (in pixel indipendenti dalla densità, dp) che consente una suddivisione.setMinSmallestWidthDp()
: imposta il valore minimo (in dp) che deve avere la dimensione di visualizzazione più piccola per attivare una suddivisione indipendentemente dall'orientamento del dispositivo.setMaxAspectRatioInPortrait()
: imposta il formato massimo della visualizzazione (altezza:larghezza) in orientamento verticale per cui vengono visualizzate le suddivisioni delle attività. Se le proporzioni di un display in formato verticale superano le proporzioni massime, le suddivisioni vengono disattivate indipendentemente dalla larghezza del display. Nota:il valore predefinito è 1,4, il che fa sì che le attività occupino l'intera finestra delle attività in orientamento verticale sulla maggior parte dei tablet. Vedi ancheSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
esetMaxAspectRatioInLandscape()
. Il valore predefinito per landscape èALWAYS_ALLOW
.setFinishPrimaryWithSecondary()
: imposta il modo in cui il completamento di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principale.NEVER
indica che il sistema non deve completare le attività principali al termine di tutte le attività nel contenitore secondario (vedi Completare le attività).setFinishSecondaryWithPrimary()
: imposta il modo in cui il completamento di tutte le attività nel contenitore principale influisce sulle attività nel contenitore secondario.ALWAYS
indica che il sistema deve sempre completare le attività nel contenitore secondario quando tutte le attività nel contenitore principale sono state completate (vedi Completare le attività).setClearTop()
: specifica se tutte le attività nel contenuto secondario sono terminate quando viene avviata una nuova attività nel contenuto. Un valorefalse
specifica che le nuove attività vengono impilate sulle attività già presenti nel contenitore secondario.
Recupera l'istanza singleton di WindowManager
RuleController
e aggiungi la regola:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
Crea un segnaposto per il contenitore secondario quando i contenuti non sono disponibili:
Crea un
ActivityFilter
che identifichi l'attività con cui il segnaposto condivide una suddivisione della finestra delle attività:Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
Aggiungi il filtro a un insieme di filtri:
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
Crea un
SplitPlaceholderRule
:Kotlin
val splitPlaceholderRule = SplitPlaceholderRule.Builder( placeholderActivityFilterSet, Intent(context, PlaceholderActivity::class.java) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build()
Java
SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder( placeholderActivityFilterSet, new Intent(context, PlaceholderActivity.class) ).setDefaultSplitAttributes(splitAttributes) .setMinWidthDp(840) .setMinSmallestWidthDp(600) .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f)) .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS) .setSticky(false) .build();
SplitPlaceholderRule.Builder
crea e configura la regola:placeholderActivityFilterSet
: contiene filtri attività che determinano quando applicare la regola identificando le attività a cui è associata l'attività segnaposto.Intent
: specifica il lancio dell'attività segnaposto.setDefaultSplitAttributes()
: applica gli attributi di layout alla regola.setMinWidthDp()
: imposta la larghezza minima del display (in pixel indipendenti dalla densità, dp) che consente una suddivisione.setMinSmallestWidthDp()
: imposta il valore minimo (in dp) che deve avere la più piccola delle due dimensioni dello schermo per consentire una suddivisione indipendentemente dall'orientamento del dispositivo.setMaxAspectRatioInPortrait()
: imposta il formato massimo dello schermo (altezza:larghezza) in formato verticale per cui vengono visualizzate le suddivisioni delle attività. Nota:il valore predefinito è 1,4, il che fa sì che le attività riempiano la finestra delle attività in formato verticale sulla maggior parte dei tablet. Vedi ancheSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
esetMaxAspectRatioInLandscape()
. Il valore predefinito per il formato orizzontale èALWAYS_ALLOW
.setFinishPrimaryWithPlaceholder()
: imposta in che modo il completamento dell'attività segnaposto influisce sulle attività nel contenitore principale. SEMPRE indica che il sistema deve sempre completare le attività nel contenitore principale al termine del segnaposto (vedi Completa attività).setSticky()
: determina se l'attività segnaposto viene visualizzata sopra la serie di attività su display di piccole dimensioni dopo la prima visualizzazione del segnaposto in una suddivisione con larghezza minima sufficiente.
Aggiungi la regola a WindowManager
RuleController
:Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
Specifica le attività che non devono mai far parte di una suddivisione:
Crea un
ActivityFilter
che identifichi un'attività che deve occupare sempre l'intera area di visualizzazione delle attività:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
Aggiungi il filtro a un insieme di filtri:
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
Crea un
ActivityRule
:Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ActivityRule.Builder
crea e configura la regola:expandedActivityFilterSet
: contiene filtri attività che determinano quando applicare la regola identificando le attività da escludere dalle suddivisioni.setAlwaysExpand()
: specifica se l'attività deve riempire l'intera finestra delle attività.
Aggiungi la regola a WindowManager
RuleController
:Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
Incorporamento tra applicazioni
Su Android 13 (livello API 33) e versioni successive, le app possono incorporare attività di altre app. L'inserimento di attività cross-application o cross-UID consente l'integrazione visiva delle attività di più applicazioni Android. Il sistema mostra sullo schermo un'attività dell'app host e un'attività incorporata di un'altra app una accanto all'altra o in alto e in basso, come nell'inserimento di attività di una singola app.
Ad esempio, l'app Impostazioni potrebbe incorporare l'attività di selezione dello sfondo dall'app WallpaperPicker:
Modello di attendibilità
I processi host che incorporano attività di altre app sono in grado di ridefinire la presentazione delle attività incorporate, incluse dimensioni, posizione, ritaglio e trasparenza. Gli host malintenzionati possono utilizzare questa funzionalità per ingannare gli utenti e creare clickjacking o altri attacchi di modifica dell'interfaccia utente.
Per evitare l'uso improprio dell'embedding delle attività tra app, Android richiede alle app di attivare la funzionalità per consentire l'embedding delle loro attività. Le app possono designare gli host come attendibili o non attendibili.
Host attendibili
Per consentire ad altre applicazioni di incorporare e controllare completamente la presentazione delle attività della tua app, specifica il certificato SHA-256 dell'applicazione host nell'attributo android:knownActivityEmbeddingCerts
degli elementi <activity>
o <application>
del file manifest della tua app.
Imposta il valore di android:knownActivityEmbeddingCerts
come stringa:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
In alternativa, per specificare più certificati, un array di stringhe:
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
che fa riferimento a una risorsa come la seguente:
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
I proprietari di app possono ottenere un digest del certificato SHA eseguendo il compito GradlesigningReport
. Il digest del certificato è l'impronta SHA-256 senza i due punti di separazione. Per saperne di più, vedi Eseguire un report sulla firma e
Autentificare il client.
Host non attendibili
Per consentire a qualsiasi app di incorporare le attività della tua app e controllarne la presentazione,
specifica l'attributo android:allowUntrustedActivityEmbedding
negli
elementi <activity>
o <application>
nel file manifest dell'app, ad esempio:
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
Il valore predefinito dell'attributo è false, il che impedisce l'embedding delle attività tra app.
Autenticazione personalizzata
Per ridurre i rischi di incorporamento di attività non attendibili, crea un
meccanismo di autenticazione personalizzato che verifichi l'identità dell'host. Se conosci i certificati dell'host, utilizza la libreria androidx.security.app.authenticator
per autenticarti. Se l'organizzatore si autentica dopo aver incorporato la tua attività, puoi visualizzare i contenuti effettivi. In caso contrario, puoi informare l'utente che l'azione non è stata consentita e bloccare i contenuti.
Utilizza il metodo ActivityEmbeddingController#isActivityEmbedded()
della libreria WindowManager di Jetpack per verificare se un host ha incorporato la tua attività, ad esempio:
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
Limitazione di dimensioni minime
Il sistema Android applica l'altezza e la larghezza minime specificate nell'elemento <layout>
del file manifest dell'app alle attività incorporate. Se un'applicazione non specifica l'altezza e la larghezza minime, vengono applicati i valori predefiniti del sistema (sw220dp
).
Se l'host tenta di ridimensionare il contenitore incorporato in modo che sia più piccolo del minimo, il contenitore incorporato si espande per occupare l'intero ambito dell'attività.
<activity-alias>
Affinché l'inserimento di attività attendibili o non attendibili funzioni con l'elemento <activity-alias>
, android:knownActivityEmbeddingCerts
o android:allowUntrustedActivityEmbedding
deve essere applicato all'attività di destinazione anziché all'alias. Il criterio che verifica la sicurezza sul server di sistema si basa sui flag impostati sul target, non sull'alias.
Applicazione host
Le applicazioni host implementano l'inserimento di attività cross-app nello stesso modo in cui implementano l'inserimento di attività di singole app. Gli oggetti SplitPairRule
e
SplitPairFilter
o ActivityRule
e ActivityFilter
specificano le attività incorporate e le suddivisioni della finestra delle attività. Le regole di suddivisione sono definite
staticamente in XML o in fase di esecuzione utilizzando le chiamate API
WindowManager di Jetpack.
Se un'applicazione host tenta di incorporare un'attività che non ha attivato l'inserimento in più app, l'attività occupa l'intero ambito dell'attività. Di conseguenza, le applicazioni host devono sapere se le attività target consentono l'embedding tra app.
Se un'attività incorporata avvia una nuova attività nella stessa attività e la nuova attività non ha attivato l'incorporamento tra app, l'attività occupa l'intero ambito dell'attività anziché sovrapporla nel contenitore incorporato.
Un'applicazione host può incorporare le proprie attività senza restrizioni, a condizione che le attività vengano avviate nella stessa attività.
Esempi di suddivisione
Suddividere lo schermo dalla finestra intera
Non è necessario il refactoring. Puoi definire la configurazione della suddivisione staticamente o in fase di esecuzione e poi chiamare Context#startActivity()
senza parametri aggiuntivi.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Suddiviso per impostazione predefinita
Quando la pagina di destinazione di un'applicazione è progettata per essere suddivisa in due contenitori su schermi di grandi dimensioni, l'esperienza utente è ottimale quando entrambe le attività vengono create e presentate contemporaneamente. Tuttavia, i contenuti potrebbero non essere disponibili per il contenitore secondario della suddivisione finché l'utente non interagisce con l'attività nel contenitore principale (ad esempio, seleziona un elemento da un menu di navigazione). Un'attività segnaposto può colmare il vuoto fino a quando i contenuti non possono essere visualizzati nel contenitore secondario della suddivisione (consulta la sezione Segnaposto).
Per creare una suddivisione con un segnaposto, crea un segnaposto e associalo all'attività principale:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
Suddivisione dei link diretti
Quando un'app riceve un'intent, l'attività target può essere mostrata come parte secondaria di una suddivisione dell'attività, ad esempio una richiesta di visualizzazione di una schermata dettagliata con informazioni su un elemento di un elenco. Sui display piccoli, i dettagli vengono visualizzati nella finestra completa dell'attività; sui dispositivi più grandi, accanto all'elenco.
La richiesta di lancio deve essere indirizzata all'attività principale e l'attività dettagli target deve essere lanciata in un'organizzazione in batch. Il sistema sceglie automaticamente la presentazione corretta, affiancata o in pila, in base alla larghezza di visualizzazione disponibile.
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)); }
La destinazione del link diretto potrebbe essere l'unica attività che deve essere disponibile per l'utente nella pila di navigazione a ritroso e ti consigliamo di evitare di chiudere l'attività dei dettagli e lasciare solo l'attività principale:
Invece, puoi completare entrambe le attività contemporaneamente utilizzando l'attributo
finishPrimaryWithSecondary
:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
Consulta la sezione Attributi di configurazione.
Più attività in container suddivisi
L'accatastamento di più attività in un contenitore diviso consente agli utenti di accedere a contenuti approfonditi. Ad esempio, con una suddivisione in elenco e dettagli, l'utente potrebbe dover accedere a una sezione di dettagli secondari, ma mantenere attiva l'attività principale:
Kotlin
class DetailActivity { . . . fun onOpenSubDetail() { startActivity(Intent(this, SubDetailActivity::class.java)) } }
Java
public class DetailActivity { . . . void onOpenSubDetail() { startActivity(new Intent(this, SubDetailActivity.class)); } }
L'attività di dettaglio secondario viene posizionata sopra l'attività di dettaglio, nascondendola:
L'utente può quindi tornare al livello di dettaglio precedente tornando indietro tramite la serie:
L'accatastamento delle attività è il comportamento predefinito quando le attività vengono avviate da un'attività nello stesso contenitore secondario. Anche le attività avviate dal contenitore principale all'interno di una suddivisione attiva vengono inserite nel contenitore secondario nella parte superiore della serie di attività.
Attività in una nuova attività
Quando le attività in una finestra di attività divisa avviano attività in una nuova attività, la nuova attività è separata dall'attività che include la suddivisione e viene visualizzata in una finestra completa. La schermata Recenti mostra due attività: l'attività nella suddivisione e la nuova attività.
Sostituzione dell'attività
Le attività possono essere sostituite nello stack del contenitore secondario, ad esempio quando l'attività principale viene utilizzata per la navigazione di primo livello e l'attività secondaria è una destinazione selezionata. Ogni selezione dal riquadro di navigazione di primo livello deve avviare una nuova attività nel contenitore secondario e rimuovere l'attività o le attività presenti in precedenza.
Se l'app non completa l'attività nel contenitore secondario quando la selezione di navigazione cambia, la navigazione a ritroso potrebbe creare confusione quando la suddivisione è compressa (quando il dispositivo è piegato). Ad esempio, se hai un menu nel riquadro principale e le schermate A e B impilate nel riquadro secondario, quando l'utente chiude lo smartphone, la schermata B si trova sopra la schermata A e questa sopra il menu. Quando l'utente torna da B, viene visualizzato A anziché il menu.
In questi casi, la schermata A deve essere rimossa dalla pila precedente.
Il comportamento predefinito quando si avvia un nuovo contenitore a lato di una suddivisione esistente è posizionare i nuovi contenitori secondari in alto e mantenere quelli vecchi nella pila posteriore. Puoi configurare le suddivisioni in modo da eliminare i container secondari precedenti con clearTop
e avviare nuove attività normalmente.
<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))); } }
In alternativa, utilizza la stessa attività secondaria e dall'attività principale (menu) invia nuovi intent che risolvono nella stessa istanza, ma attivano un aggiornamento dello stato o dell'interfaccia utente nel contenitore secondario.
Più suddivisioni
Le app possono fornire una navigazione approfondita a più livelli avviando attività aggiuntive sul lato.
Quando un'attività in un contenitore secondario avvia una nuova attività a lato, viene creata una nuova suddivisione sopra quella esistente.
La pila di ritorno contiene tutte le attività aperte in precedenza, quindi gli utenti possono accedere alla suddivisione A/B dopo aver completato C.
Per creare una nuova suddivisione, avvia la nuova attività a lato del contenitore secondario esistente. Dichiara le configurazioni per le suddivisioni A/B e B/C e avvia normalmente l'attività C da 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)); } }
Rispondere alle modifiche dello stato di suddivisione
Attività diverse in un'app possono avere elementi dell'interfaccia utente che svolgono la stessa funzione, ad esempio un controllo che apre una finestra contenente le impostazioni dell'account.
Se due attività che hanno un elemento dell'interfaccia utente in comune sono in una suddivisione, è ridondante e forse fonte di confusione mostrare l'elemento in entrambe le attività.
Per sapere quando le attività sono in una suddivisione, controlla il flusso
SplitController.splitInfoList
o registra un ascoltatore con
SplitControllerCallbackAdapter
per le modifiche dello stato della suddivisione. Quindi,
modifica l'interfaccia utente di conseguenza:
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); }); }
Le coroutine possono essere avviate in qualsiasi stato del ciclo di vita, ma in genere vengono avviate nello stato STARTED
per risparmiare risorse (per ulteriori informazioni, consulta Utilizzare le coroutine Kotlin con componenti consapevoli del ciclo di vita).
I richiami possono essere effettuati in qualsiasi stato del ciclo di vita, anche quando un'attività viene interrotta. In genere, gli ascoltatori devono essere registrati in onStart()
e non registrati in onStop()
.
Modale a finestra intera
Alcune attività impediscono agli utenti di interagire con l'applicazione finché non viene eseguita un'azione specifica, ad esempio un'attività della schermata di accesso, una schermata di conferma delle norme o un messaggio di errore. Le attività modali devono essere impedite da comparire in una suddivisione.
È possibile forzare un'attività a riempire sempre la finestra delle attività utilizzando la configurazione di espansione:
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Completare le attività
Gli utenti possono completare le attività su entrambi i lati della suddivisione scorrendo dal bordo del display:
Se il dispositivo è configurato per utilizzare il pulsante Indietro anziché la navigazione con i gesti, l'input viene inviato all'attività attiva, ovvero l'attività toccata o avviata per ultima.
L'effetto della terminazione di tutte le attività in un contenitore sul contenitore opposto dipende dalla configurazione della suddivisione.
Attributi di configurazione
Puoi specificare gli attributi della regola della coppia di suddivisione per configurare in che modo il completamento di tutte le attività su un lato della suddivisione influisce sulle attività sull'altro lato della suddivisione. Gli attributi sono:
window:finishPrimaryWithSecondary
- In che modo il completamento di tutte le attività nel contenitore secondario influisce sulle attività nel contenitore principalewindow:finishSecondaryWithPrimary
- In che modo il completamento di tutte le attività nel contenitore principale influisce sulle attività nel contenitore secondario
I valori possibili degli attributi includono:
always
: completa sempre le attività nel contenitore associatonever
: non completare mai le attività nel contenitore associatoadjacent
: completa le attività nel contenitore associato quando i due contenitori sono visualizzati uno accanto all'altro, ma non quando sono impilati
Ad esempio:
<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>
Configurazione predefinita
Quando tutte le attività in un contenitore di una suddivisione terminano, il contenitore rimanente occupa l'intera finestra:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Completare le attività insieme
Termina automaticamente le attività nel contenitore principale quando tutte le attività nel contenitore secondario sono terminate:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Completa automaticamente le attività nel contenitore secondario al termine di tutte le attività nel contenitore principale:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Terminare le attività insieme al termine di tutte le attività nel contenitore principale o secondario:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Completare più attività nei contenitori
Se in un contenitore diviso sono impilate più attività, il completamento di un'attività nella parte inferiore della pila non completa automaticamente le attività in alto.
Ad esempio, se nel contenitore secondario sono presenti due attività, C sopra B:
e la configurazione della suddivisione è definita dalla configurazione delle attività A e B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
se completi l'attività principale, la suddivisione viene mantenuta.
Il completamento dell'attività di base (principale) del contenitore secondario non rimuove le attività al di sopra e, di conseguenza, mantiene anche la suddivisione.
Vengono eseguite anche eventuali regole aggiuntive per completare le attività insieme, ad esempio completare l'attività secondaria con quella principale:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
E quando la suddivisione è configurata per completare la primaria e la secondaria insieme:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Modificare le proprietà di suddivisione in fase di esecuzione
Le proprietà di una segmentazione attiva e visibile non possono essere modificate. La modifica delle regole di suddivisione influisce su nuovi lanci di attività e nuovi contenitori, ma non sulle suddivisioni esistenti e attive.
Per modificare le proprietà delle suddivisioni attive, completa l'attività o le attività secondarie nella suddivisione e riavvia la visualizzazione laterale con una nuova configurazione.
Proprietà di suddivisione dinamica
Android 15 (livello API 35) e versioni successive supportate da Jetpack WindowManager 1.4 e versioni successive offrono funzionalità dinamiche che consentono la configurabilità delle suddivisioni di embedding delle attività, tra cui:
- Espansione dei riquadri:un divisore interattivo e trascinabile consente agli utenti di ridimensionare i riquadri in una presentazione divisa.
- Fissare la serie di attività:gli utenti possono fissare i contenuti in un contenitore e isolare la navigazione in questo contenitore da quella nell'altro contenitore.
- Diminuzione della luminosità a schermo intero della finestra di dialogo: quando viene visualizzata una finestra di dialogo, le app possono specificare se attenuare l'intera finestra dell'attività o solo il contenitore che ha aperto la finestra di dialogo.
Espansione del riquadro
L'espansione del riquadro consente agli utenti di regolare la quantità di spazio sullo schermo assegnata alle due attività in un layout a due riquadri.
Per personalizzare l'aspetto del divisore della finestra e impostarne l'intervallo scorrevole:
Crea un'istanza di
DividerAttributes
Personalizza gli attributi del divisore:
color
: il colore del separatore del riquadro trascinatile.widthDp
: la larghezza del separatore del riquadro trascinatile. Imposta suWIDTH_SYSTEM_DEFAULT
per consentire al sistema di determinare la larghezza del separatore.Intervallo di trascinamento:la percentuale minima dello schermo che ciascun riquadro può occupare. Può variare da 0,33 a 0,66. Imposta su
DRAG_RANGE_SYSTEM_DEFAULT
per consentire al sistema di determinare l'intervallo di trascinamento.
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(context, 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(context, R.color.divider_color)) .setWidthDp(4) .setDragRange(DividerAttributes.DragRange.DRAG_RANGE_SYSTEM_DEFAULT) .build() ); } SplitAttributes splitAttributes = splitAttributesBuilder.build();
Blocco della pila di attività
La funzionalità di blocco della serie di attività consente agli utenti di bloccare una delle finestre divise in modo che l'attività rimanga invariata mentre gli utenti navigano nell'altra finestra. La funzionalità di bloccamento della serie attività offre un'esperienza di multitasking avanzata.
Per attivare il bloccaggio della pila di attività nella tua app:
Aggiungi un pulsante al file di layout dell'attività che vuoi bloccare, ad esempio l'attività di dettaglio di un layout elenco dettagliato:
<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>
Nel metodo
onCreate()
dell'attività, imposta un listener onclick sul pulsante:Kotlin
pinButton = 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); });
Diminuzione della luminosità a schermo intero della finestra di dialogo
In genere, le attività attenuano i display per attirare l'attenzione su una finestra di dialogo. Per un'esperienza UI unificata, quando viene incorporata un'attività, entrambi i riquadri del display a doppio riquadro devono essere attenuati, non solo il riquadro contenente l'attività che ha aperto la finestra di dialogo.
Con WindowManager 1.4 e versioni successive, l'intera finestra dell'app si attenua per impostazione predefinita quando si apre una finestra di dialogo (vedi EmbeddingConfiguration.DimAreaBehavior.ON_TASK
).
Per attenuare solo il contenitore dell'attività che ha aperto la finestra di dialogo, usa
EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK
.
Estrarre un'attività da una finestra divisa a una finestra completa
Crea una nuova configurazione che mostri la finestra completa dell'attività secondaria, quindi riavvia l'attività con un'intenzione che risolva nella stessa istanza.
Verificare il supporto della suddivisione in fase di esecuzione
L'inserimento di attività è supportato su Android 12L (livello API 32) e versioni successive, ma è anche disponibile su alcuni dispositivi con versioni precedenti della piattaforma. Per verificare la disponibilità della funzionalità in fase di esecuzione, utilizza la proprietà SplitController.splitSupportStatus
o il metodo 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. }
Se le suddivisioni non sono supportate, le attività vengono avviate sopra la serie di attività (secondo il modello di embedding non di attività).
Impedire l'override del sistema
I produttori di dispositivi Android (produttori di apparecchiature originali o OEM) possono implementare l'inserimento di attività come funzione del sistema del dispositivo. Il sistema specifica le regole di suddivisione per le app con più attività, sostituendo il comportamento di gestione delle finestre delle app. L'override del sistema forza le app con più attività in una modalità di incorporamento delle attività definita dal sistema.
L'inserimento di attività di sistema può migliorare la presentazione dell'app tramite layout con più riquadri, ad esempio elenco-dettaglio, senza apportare modifiche all'app. Tuttavia, l'inserimento di attività del sistema potrebbe anche causare layout dell'app errati, bug o conflitti con l'inserimento di attività implementato dall'app.
L'app può impedire o consentire l'inserimento di attività di sistema impostando una proprietà nel file manifest dell'app, ad esempio:
<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>
Il nome della proprietà è definito nell'oggetto WindowManager WindowProperties
di Jetpack. Imposta il valore su false
se la tua app implementa l'inserimento di attività o se vuoi impedire al sistema di applicare le sue regole di inserimento di attività alla tua app. Imposta il valore su false
per consentire al sistema di applicare all'app l'inserimento di attività definito dal sistema.true
Limitazioni, restrizioni e avvertenze
- Solo l'app host dell'attività, identificata come proprietaria dell'attività principale nell'attività, può organizzare e incorporare altre attività nell'attività. Se le attività che supportano l'inserimento e le suddivisioni vengono eseguite in un'attività appartenente a un'altra applicazione, l'inserimento e le suddivisioni non funzioneranno per queste attività.
- Le attività possono essere organizzate solo all'interno di un'unica attività. L'avvio di un'attività in una nuova attività la inserisce sempre in una nuova finestra espansa al di fuori di eventuali suddivisioni esistenti.
- Solo le attività nello stesso processo possono essere organizzate e suddivise. Il callback
SplitInfo
registra solo le attività che appartengono allo stesso processo, poiché non è possibile conoscere le attività in processi diversi. - Ogni coppia o singola regola di attività si applica solo ai lanci di attività che si verificano dopo la registrazione della regola. Al momento non è possibile aggiornare le suddivisioni esistenti o le relative proprietà visive.
- La configurazione del filtro delle coppie divise deve corrispondere alle intenzioni utilizzate per il lancio completo delle attività. La corrispondenza avviene nel momento in cui viene avviata una nuova attività dal processo di applicazione, pertanto potrebbe non essere a conoscenza dei nomi dei componenti che vengono risolti in un secondo momento nel processo di sistema quando si utilizzano intent impliciti. Se il nome di un componente non è noto al momento del lancio, è possibile utilizzare un carattere jolly ("*/*") e applicare un filtro in base all'azione dell'intent.
- Al momento non è possibile spostare le attività tra i contenitori o all'interno e all'esterno delle suddivisioni dopo la loro creazione. Le suddivisioni vengono create dalla raccolta WindowManager solo quando vengono avviate nuove attività con regole di corrispondenza e vengono distrutte al termine dell'ultima attività in un contenitore suddiviso.
- Le attività possono essere riavviate quando la configurazione cambia, quindi quando viene creata o rimossa una suddivisione e i limiti dell'attività cambiano, l'attività può essere completamente distrutta e creata una nuova. Di conseguenza, gli sviluppatori di app devono fare attenzione a operazioni come il lancio di nuove attività dai callback del ciclo di vita.
- I dispositivi devono includere l'interfaccia delle estensioni della finestra per supportare l'embedding delle attività. Quasi tutti i dispositivi con schermo di grandi dimensioni con Android 12L (livello API 32) o versioni successive includono l'interfaccia. Tuttavia, alcuni dispositivi con schermi di grandi dimensioni che non sono in grado di eseguire più attività non includono l'interfaccia delle estensioni della finestra. Se un dispositivo con schermo di grandi dimensioni non supporta la modalità multi-finestra, potrebbe non supportare l'inserimento di attività.
Risorse aggiuntive
- Codelab:
- Percorso di apprendimento: Incorporazione delle attività
- App di esempio: activity-embedding