Incorporamento delle attività

L'incorporamento delle attività ottimizza le app sui dispositivi con schermi grandi suddividendo un finestra delle attività dell'applicazione tra due attività o due istanze della stessa attività.

Figura 1. App Impostazioni con attività affiancate.

Se la tua app è costituita da più attività, l'incorporamento delle attività ti consente di: offrono un'esperienza utente migliorata su tablet, pieghevoli e dispositivi ChromeOS.

L'incorporamento delle attività non richiede il refactoring del codice. Sei tu a stabilire in che modo la tua app ne visualizza le attività, affiancate o in pila, creando un file XML di configurazione del deployment o effettuando chiamate API Jetpack WindowManager.

Il supporto per 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. Attivato schermi di grandi dimensioni, le attività vengono visualizzate una accanto all'altra. Il sistema determina presentazione basata sulla configurazione creata (senza logica di diramazione) obbligatorio.

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 piega e si apre.

L'incorporamento delle attività è supportato sulla maggior parte dei dispositivi con schermi di grandi dimensioni con Android 12L (livello API 32) e superiore.

Finestra 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 raggruppate nel container secondario quando vengono avviate e il container secondario è impilato sopra il container principale su schermi di piccole dimensioni in modo che la sovrapposizione delle attività e la navigazione a ritroso siano coerenti con l'ordine dei attività già integrate nella tua app.

L'incorporamento delle attività ti consente di visualizzare le attività in diversi modi. Il tuo l'app può dividere la finestra delle attività avviando due attività una accanto all'altra contemporaneamente:

Figura 2. Due attività affiancate.

Oppure, un'attività che occupa l'intera finestra dell'attività può creare una suddivisione per lanciare una nuova attività insieme a:

Figura 3. L'attività A inizia l'attività B a lato.

Le attività che sono già in una suddivisione e condividono una finestra delle attività possono avviare altre attività nei seguenti modi:

  • A lato sopra un'altra attività:

    Figura 4. L'attività A avvia l'attività C a lato dell'attività B.
  • Di lato e sposta la divisione lateralmente, nascondendo la precedente attività:

    Figura 5. L'attività B avvia l'attività C di lato e sposta la divisi lateralmente.
  • Avvia un'attività in primo piano, ovvero nello stesso stack di attività:

    Figura 6. L'attività B avvia l'attività C senza flag intent aggiuntivi.
  • Avvia una finestra completa dell'attività nella stessa attività:

    Figura 7. L'attività A o B inizia l'attività C che si riempie finestra dell'attività.

Navigazione a ritroso

Tipi di applicazioni diversi possono avere diverse regole di navigazione a ritroso in lo stato della finestra di attività in base alle dipendenze tra le attività o al modo Gli utenti attivano l'evento Indietro, ad esempio:

  • Insieme: se le attività sono correlate e non dovrebbe essere mostrata nessuna senza l'altra, è possibile configurare la navigazione a ritroso per terminare entrambe.
  • Per farlo da soli: se le attività sono completamente indipendenti, torna alla navigazione su una l'attività non influisce sullo stato di un'altra attività nella finestra dell'attività.

L'evento Indietro viene inviato all'ultima attività attiva quando viene usato il pulsante per la navigazione.

Per la navigazione basata su gesti:

  • Android 14 (livello API 34) e versioni precedenti: l'evento Indietro viene inviato al attività in cui si è verificato il gesto. Quando gli utenti scorrono dal lato sinistro sullo schermo, l'evento Indietro viene inviato all'attività nel riquadro sinistra della finestra divisa. Quando gli utenti fanno scorrere il dito dal lato destro l'evento Indietro viene inviato all'attività nel riquadro di 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.

    • In scenari che coinvolgono due attività da app diverse (overlay), l'evento dorso è diretto all'ultima attività in primo piano, in linea con comportamento della navigazione con pulsanti.

Layout a più riquadri

Jetpack WindowManager consente di creare un'attività di incorporamento di più riquadri layout su dispositivi con schermi grandi con Android 12L (livello API 32) o versioni successive 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 un'esperienza di alta qualità presentazione, il sistema avvia l'attività di elenco e quindi 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.

Figura 8. Due attività avviate simultaneamente in un riquadro multiplo layout.

Suddividi attributi

Puoi specificare la proporzione della finestra dell'attività tra i container suddivisi e la disposizione dei container l'uno rispetto 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 destra
    • rtl: da destra a sinistra
    • locale: ltr o rtl 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:

Per esempi, consulta la sezione API WindowManager.

Figura 9. Due suddivisioni dell'attività disposte da sinistra a destra, ma con rapporti di suddivisione diversi.

Segnaposto

Le attività segnaposto sono attività secondarie vuote che occupano un'area di suddivisione attività. In ultima analisi, sono pensati per essere sostituiti con un'altra attività che includono 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 terminano automaticamente quando vengono impostate le dimensioni di visualizzazione la larghezza o l'altezza è troppo ridotta per visualizzare una suddivisione. Quando lo spazio lo consente, il sistema riavvia il segnaposto con uno stato reinizializzato.

Figura 10. Dispositivo pieghevole che si piega e si apre. Segnaposto l'attività viene completata e ricreata come modifiche delle dimensioni di visualizzazione.

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 pari a true, l'oggetto visualizza il segnaposto come attività di livello più alto nella finestra dell'attività quando il display viene ridimensionato a un riquadro singolo da un display a due riquadri (consulta Configurazione di suddivisione per un esempio).

Figura 11. Dispositivo pieghevole che si piega e si apre. Segnaposto l'attività è fissa.

Modifiche alle dimensioni delle finestre

Quando le modifiche alla configurazione del dispositivo riducono la larghezza della finestra delle attività in modo che non abbastanza grande per un layout a più riquadri (ad esempio, quando uno schermo pieghevole di grandi dimensioni il dispositivo si piega dalle dimensioni del tablet a quelle del telefono oppure la finestra dell'app viene ridimensionata in modalità multi-finestra), le attività non segnaposto nel riquadro secondario della finestra delle attività si sovrappone alle 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 nella 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 loco senza ulteriori flag di intent:

Suddivisione delle attività contenente le attività A, B e C con C sovrapposta a B.

generando il seguente ordine z di attività nella stessa attività:

Stack di attività secondarie contenente l'attività C sovrapposta a B.
          Lo stack secondario è sovrapposto allo stack di attività principali
          contenente l'attività A.

Di conseguenza, in una finestra delle attività più piccola, l'applicazione si riduce a una singola attività con C nella parte superiore dello stack:

Piccola finestra che mostra solo l'attività C.

Se torni indietro nella finestra più piccola, puoi spostarti tra le attività impilate una sopra l'altra.

Se la configurazione della finestra delle attività viene ripristinata a una dimensione maggiore che può possono ospitare più riquadri, le attività vengono visualizzate di nuovo una accanto all'altra.

Suddivisione in pila

L'attività B inizia l'attività C di lato e sposta la divisione lateralmente:

Finestra dell'attività che mostra le attività A e B, poi le attività B e C.

Il risultato è il seguente ordine Z delle attività nella stessa attività:

Attività A, B e C in un unico elenco. Le attività sono raggruppate
          nel seguente ordine dall'alto verso il basso: C, B, A.

In una finestra dell'attività più piccola, l'applicazione si riduce a una singola attività con C attivato in alto:

Piccola finestra che mostra solo l'attività C.

Orientamento verticale fisso

L'impostazione del file manifest android:screenOrientation consente alle app di limitare l'orientamento verticale o orizzontale. Per migliorare l'esperienza utente su dispositivi con schermi di grandi dimensioni come tablet e pieghevoli, i produttori di dispositivi Gli OEM possono ignorare le richieste di orientamento dello schermo e scrivere l'app in verticale. orientamento orizzontale per le creatività orizzontali e orientamento orizzontale per le creatività verticali.

Figura 12. Attività in formato Letterbox: verticale su dispositivo orizzontale (sinistra), orizzontale su dispositivo verticale (destra).

Analogamente, quando l'inserimento di attività è attivato, 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.

Figura 13. L'attività A verticale fisso avvia l'attività B di lato.

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 su cui l'app ha implementato l'incorporamento dell'attività.

Procedi nel seguente modo:

  1. Aggiungi la dipendenza più recente della libreria WindowManager a livello di modulo della tua app build.gradle, ad esempio:

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

    La libreria WindowManager fornisce tutti i componenti necessari per l'embedding delle attività.

  2. 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 verticale sui display orizzontali per orientarla in vista del passaggio 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:

  1. 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 di la 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>
    
  2. Crea un inizializzatore.

    Il componente WindowManager RuleController analizza il file di configurazione XML e rende le regole disponibili per il sistema. Una libreria Startup di Jetpack Initializer rende disponibile il file XML per RuleController all'avvio dell'app in modo che le regole siano in vigore all'avvio di qualsiasi attività.

    Per creare un inizializzatore, segui questi passaggi:

    1. Aggiungi la dipendenza più recente della libreria Jetpack Startup a livello di modulo build.gradle, ad esempio:

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

    2. Crea una classe che implementi l'interfaccia Initializer.

      L'inizializzatore rende le regole di suddivisione disponibili per RuleController per passando l'ID del file di configurazione XML (main_split_config.xml) al metodo RuleController.parseRules().

      Kotlin

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      Java

      public class SplitInitializer implements Initializer<RuleController> {
      
           @NonNull
           @Override
           public RuleController create(@NonNull Context context) {
               RuleController ruleController = RuleController.getInstance(context);
               ruleController.setRules(
                   RuleController.parseRules(context, R.xml.main_split_config)
               );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }
  3. 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'inizializzatore RuleController, SplitInitializer:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    InitializationProvider rileva e inizializza SplitInitializer prima viene chiamato il metodo onCreate() 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:

  1. Crea una regola di suddivisione:

    1. Crea una SplitPairFilter che identifica 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
      );
    2. Aggiungi il filtro a un insieme di filtri:

      Kotlin

      val filterSet = setOf(splitPairFilter)

      Java

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. Crea attributi di 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 layout attributi:

      • setSplitType(): definisce in che modo l'area di visualizzazione disponibile viene assegnata a ciascun contenitore di attività. Il tipo di suddivisione rapporto specifica la proporzione dell'area di visualizzazione disponibile assegnata container principale; il container secondario occupa il resto l'area di visualizzazione disponibile.
      • setLayoutDirection(): specifica la disposizione dei contenitori delle attività rispetto l'uno all'altro, prima il contenitore principale.
    4. Crea una 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 sezione personalizzata.
      • setMinWidthDp(): imposta la larghezza di visualizzazione minima (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 massimo aspetto di visualizzazione rapporto (altezza:larghezza) con orientamento verticale per quale attività i segmenti di pubblico. 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 comporta che le attività occupino l'intera finestra delle attività in orientamento verticale sulla maggior parte dei tablet. Vedi anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT e setMaxAspectRatioInLandscape(). 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 termina le attività nel contenitore secondario quando tutte le attività nella finitura del container principale (vedi Completa le attività).
      • setClearTop(): specifica se tutte le attività in il container secondario termina quando viene avviata una nuova attività nel container. Un valore false specifica che le nuove attività vengono impilate sulle attività già presenti nel contenitore secondario.
    5. 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);
        
  2. Crea un segnaposto per il contenitore secondario quando contenuti non disponibili:

    1. Crea un elemento ActivityFilter che identifichi l'attività con cui Il segnaposto condivide una suddivisione della finestra dell'attività:

      Kotlin

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

      Java

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. Aggiungi il filtro a un insieme di filtri:

      Kotlin

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      Java

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. 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 di attività che determinare quando applicare la regola identificando le attività con 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 di visualizzazione minima (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 le proporzioni di visualizzazione massime (altezza:larghezza) in verticale orientamento per il quale 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 anche SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT e setMaxAspectRatioInLandscape(). 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 che il segnaposto è stato visualizzato per la prima volta in una suddivisione con una larghezza minima sufficiente.
    4. Aggiungi la regola a WindowManager RuleController:

      Kotlin

      ruleController.addRule(splitPlaceholderRule)

      Java

      ruleController.addRule(splitPlaceholderRule);
  3. Specifica le attività che non devono mai far parte di una suddivisione:

    1. 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
      );
    2. Aggiungi il filtro a un insieme di filtri:

      Kotlin

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      Java

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. 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à.
    4. 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 altri app. Incorporamento delle attività tra applicazioni o tra UID consente l'integrazione visiva delle attività di più applicazioni Android. La visualizza un'attività dell'app host e un'attività incorporata da un'altra app sullo schermo, affiancata o in alto e in basso, come nelle app singole incorporamento dell'attività.

Ad esempio, l'app Impostazioni potrebbe incorporare l'attività di selezione dello sfondo da l'app BackgroundPicker:

Figura 14. App Impostazioni (menu a sinistra) con selettore sfondo come attività incorporata (a destra).

Modello di attendibilità

I processi host che incorporano attività di altre app sono in grado di ridefinire il della presentazione delle attività incorporate, tra cui dimensioni, posizione, ritaglio e e trasparenza. Gli host dannosi possono utilizzare questa funzionalità per ingannare gli utenti e creare clickjacking o altri attacchi di correzione dell'interfaccia utente.

Per evitare l'uso improprio dell'inserimento di attività tra app, Android richiede alle app di attivare la funzionalità per consentire l'inserimento 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 dei di attività dalla tua app, specifica il certificato SHA-256 dell'host nell'attributo android:knownActivityEmbeddingCerts della 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 comando Gradle signingReport attività. Il digest del certificato è l'impronta SHA-256 senza i due punti separati. 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 l'host , utilizza la libreria androidx.security.app.authenticator per autenticarsi. Se l'host autentica dopo aver incorporato la tua attività, puoi per visualizzare i contenuti effettivi. In caso contrario, puoi informare l'utente che l'azione è stata non consentito e blocca i contenuti.

Utilizza il metodo ActivityEmbeddingController#isActivityEmbedded() dal la libreria Jetpack WindowManager per verificare se un host sta incorporando dell'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 a una dimensione inferiore a quella minimo, il container incorporato si espande per occupare tutti i limiti delle attività.

<activity-alias>

Affinché l'incorporamento di attività attendibili o non attendibili funzioni con il Elemento <activity-alias>, android:knownActivityEmbeddingCerts o android:allowUntrustedActivityEmbedding deve essere applicato all'attività target anziché l'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. SplitPairRule e Oggetti SplitPairFilter o ActivityRule e ActivityFilter per specificare le attività incorporate e la suddivisione delle finestre delle attività. Le regole di suddivisione sono definite staticamente in XML o in fase di runtime, utilizzando Jetpack Chiamate API WindowManager.

Se un'applicazione host tenta di incorporare un'attività per la quale non è stata attivata l'incorporamento cross-app, l'attività occupa tutti i limiti delle attività. Di conseguenza, le applicazioni host devono sapere se le attività target consentono incorporamento.

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 purché le attività vengono avviate nella stessa attività.

Esempi di suddivisione

Suddividere lo schermo dalla finestra intera

Figura 15. L'attività A avvia l'attività B a lato.

Non è richiesto alcun refactoring. Puoi definire la configurazione della suddivisione in modo statico o in fase di runtime, quindi chiama Context#startActivity() senza parametri aggiuntivi.

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

Dividi 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 è migliore quando entrambe le attività vengono creati e presentati 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).

Figura 16. Suddivisione creata aprendo due attività contemporaneamente. Un'attività è un 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>

Quando un'app riceve un intent, l'attività target può essere mostrata come parte secondaria di una suddivisione attività; Ad esempio, per mostrare un dettaglio schermata 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.

Figura 17. Attività dei dettagli dei link diretti mostrata da sola su uno schermo piccolo, ma insieme a un'attività di elenco su uno schermo di grandi dimensioni.

La richiesta di lancio deve essere indirizzata all'attività principale e ai dettagli della destinazione l'attività deve essere lanciata in una suddivisione. Il sistema sceglie automaticamente presentazione corretta, in pila o affiancata, in base alle larghezza di visualizzazione.

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 dovrebbe essere disponibile l'utente nello stack di navigazione a ritroso, pertanto è consigliabile evitare l'attività di dettaglio, lasciando solo l'attività principale:

Display grande con attività dell&#39;elenco e dettagli delle attività affiancate.
          La navigazione a ritroso non consente di ignorare l&#39;attività dettagliata e di uscire dall&#39;elenco
          l&#39;attività sullo schermo.

Display piccolo con solo attività dettagliata. Impossibile eseguire la navigazione a ritroso
          ignora attività dettagliate e rivela attività elenco.

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 elenco-dettagli, l'utente potrebbe dover accedere a una sezione dei dettagli secondari, ma mantieni attiva l'attività principale:

Figura 18. L'attività si apre in posizione nel riquadro secondario della finestra delle attività.

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

Figura 19. Attività rimossa dalla parte superiore dell'elenco filtri.

Sovrapporre le attività una sopra l'altra è il comportamento predefinito quando vengono avviate da un'attività nello stesso contenitore secondario. Attività avviato dal container principale all'interno di un segmento attivo, container secondario in cima all'elenco 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. Nella schermata Recenti vengono visualizzate due attività: l'attività nella suddivisione e la nuova attività.

Figura 20. Avvia l'attività C in una nuova attività dall'attività B.

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 dalla navigazione di livello superiore Avviare una nuova attività nel contenitore secondario e rimuoverla oppure attività che erano presenti in precedenza.

Figura 21. Attività di navigazione di primo livello nel riquadro principale sostituisce le attività di destinazione nel riquadro secondario.

Se l'app non completa l'attività nel contenitore secondario quando modifiche alla selezione della navigazione, la navigazione a ritroso potrebbe creare confusione quando la suddivisione è compresso (quando il dispositivo è chiuso). Ad esempio, se hai un menu nella il riquadro principale e le schermate A e B impilate nel riquadro secondario, quando l'utente Piega lo smartphone, B è sopra il tasto A e A è sopra il menu. Quando l'utente torna indietro da B, compare A al posto del menu.

In questi casi, la schermata A deve essere rimossa dalla pila precedente.

Il comportamento predefinito quando si avvia a lato in un nuovo contenitore su un la suddivisione esistente consiste nel posizionare i nuovi contenitori secondari in cima e mantenere i vecchi quelli nello stack posteriore. Puoi configurare le suddivisioni in modo da cancellare la precedente contenitori secondari con clearTop e avviano normalmente le nuove attività.

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

Figura 22. L'attività B inizia l'attività C a lato.

Lo stack posteriore contiene tutte le attività che sono state aperte in precedenza, quindi gli utenti possono vai alla sezione A/B al termine del C.

Attività A, B e C in una serie. Le attività sono impilate nel seguente ordine, dall&#39;alto verso il basso: C, B, A.

Per creare una nuova suddivisione, avvia la nuova attività di lato rispetto a quella esistente container secondario. 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));
    }
}

Reagisci alle modifiche di 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.

Figura 23. Attività diverse con elementi UI funzionalmente identici.

Se due attività che hanno un elemento dell'interfaccia utente in comune sono in una suddivisione, è redundante e forse fonte di confusione mostrare l'elemento in entrambe le attività.

Figura 24. Elementi dell'interfaccia utente duplicati nella suddivisione delle 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. Poi, regola 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 lanciate in qualsiasi stato del ciclo di vita, ma in genere vengono lanciate lo stato STARTED per risparmiare risorse (vedi Utilizzare le coroutine Kotlin con componenti sensibili al ciclo di vita per maggiori informazioni).

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().

Finestra modale a schermo intero

Alcune attività impediscono agli utenti di interagire con l'applicazione finché viene eseguita l'azione specificata; ad esempio un'attività nella schermata di accesso, schermata di conferma 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>

Termina attività

Gli utenti possono terminare le attività su entrambi i lati del segmento scorrendo dal bordo del display:

Figura 25. Gesto di scorrimento per terminare l'attività B.
Figura 26. Gesto di scorrimento per terminare l'attività A.

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 delle regole di coppia divisa per configurare il modo in cui le attività su un lato del segmento incidono sulle attività dell'altro lato del la divisione. Gli attributi sono:

  • window:finishPrimaryWithSecondary: come completare tutte le attività in il contenitore secondario influisce sulle attività nel contenitore principale
  • window: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 associato
  • never: non completa mai le attività nel contenitore associato
  • adjacent: completa le attività nel contenitore associato quando i due contenitori sono visualizzati uno accanto all'altro, ma non quando sono impilzati

Ad esempio:

<SplitPairRule
    &lt;!-- Do not finish primary container activities when all secondary container activities finish. --&gt;
    window:finishPrimaryWithSecondary="never"
    &lt;!-- Finish secondary container activities when all primary container activities finish. --&gt;
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

Configurazione predefinita

Quando tutte le attività in un container di una suddivisione terminano, il container rimanente occupa l'intera finestra:

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

Suddivisione contenente le attività A e B. A è terminata, quindi B occupa
          l&#39;intera finestra.

Suddivisione contenente le attività A e B. B termina, lasciando da A a
          per occupare l&#39;intera finestra.

Completare le attività insieme

Completa automaticamente le attività nel contenitore principale quando tutte le attività nella finitura del container secondario:

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

Suddivisione contenente le attività A e B. L&#39;attività B è stata completata, quindi anche l&#39;attività A è stata completata, lasciando vuota la finestra delle attività.

Suddivisione contenente le attività A e B. L&#39;attività A è stata completata, quindi rimane solo B
          nella finestra delle attività.

Completa automaticamente le attività nel contenitore secondario quando tutte delle attività nella fase di completamento del container principale:

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

Suddivisione contenente le attività A e B. A è finito, che a sua volta
          termina il B, lasciando vuota la finestra dell&#39;attività.

Suddivisione contenente le attività A e B. B finisce, lasciando solo A
          nella finestra dell&#39;attività.

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>

Suddivisione contenente le attività A e B. A è finito, che a sua volta
          termina il B, lasciando vuota la finestra dell&#39;attività.

Suddivisione contenente le attività A e B. ha finito B, che a sua volta
          termina A, lasciando vuota la finestra dell&#39;attività.

Completare più attività nei contenitori

Se più attività sono raggruppate in un contenitore diviso, il completamento di un'attività in fondo alla pila non completano automaticamente le attività in alto.

Ad esempio, se nel contenitore secondario si trovano due attività, C sopra la B:

Stack di attività secondarie contenente l&#39;attività C sovrapposta a B
          è sovrapposta allo stack di attività principali che contiene
          R.

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.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondaria, C sovrapposta a B. C finisce, lasciando A e B
          suddivisione attività.

Il completamento dell'attività di base (principale) del contenitore secondario non rimuove le attività al di sopra, pertanto viene mantenuta anche la suddivisione.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondaria, C sovrapposta a B. B termina, lasciando A e C nella suddivisione dell&#39;attività.

Eventuali regole aggiuntive per completare le attività insieme, ad esempio la fine dell'attività secondaria con quella principale, vengono eseguite anche:

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

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          container secondario, C sopra il contenitore B. A termina, completando anche B e C.

E quando la suddivisione è configurata per completare l'operazione principale e secondaria insieme:

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

Con l&#39;attività A nel contenitore principale e le attività B e C nel contenitore secondario, C sovrapposta a B. C termina, lasciando A e B nella suddivisione delle attività.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondaria, C sovrapposta a B. B termina, lasciando A e C nella suddivisione dell&#39;attività.

Suddividi con l&#39;attività A nel contenitore principale e le attività B e C nel
          secondaria, C sovrapposta a B. termina anche A, terminando anche B e
          C.

Modifica 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à dei segmenti attivi, termina l'attività secondaria oppure le attività del segmento e lanciarle di nuovo con una nuova configurazione.

Proprietà di suddivisione dinamica

Android 15 (livello API 35) e versioni successive supportato da Jetpack WindowManager 1.4 e superiori offrono funzionalità dinamiche che consentono la configurabilità delle attività suddivisioni di incorporamento, tra cui:

  • Espansione dei riquadri: un divisore interattivo e trascinabile consente agli utenti di ridimensionare i riquadri in una presentazione divisa.
  • Blocco dell'attività: gli utenti possono bloccare i contenuti in un contenitore e isola la navigazione nel contenitore da quella nell'altro contenitore.
  • Oscuramento della finestra di dialogo a schermo intero: quando viene visualizzata una finestra di dialogo, le app possono specificare se attenuare l'intera finestra dell'attività o solo il container che ha aperto .

Espansione 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 separatore per finestre e impostare il divisore intervallo trascinabile, procedi nel seguente modo:

  1. Crea un'istanza di DividerAttributes

  2. Personalizza gli attributi del divisore:

    • color: il colore del separatore del riquadro trascinatile.

    • widthDp:la larghezza del separatore del riquadro trascinabile. Imposta su WIDTH_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 delle attività

Il blocco attività consente agli utenti di bloccare una delle finestre divisa in modo che l'attività rimane invariata mentre gli utenti navigano all'interno dell'altra finestra. Attività Il blocco offre un'esperienza di multitasking migliorata.

Per attivare il blocco delle attività nella tua app:

  1. Aggiungi un pulsante al file di layout dell'attività che vuoi fissare, per Ad esempio, l'attività di dettaglio di un layout elenco dettagli:

    <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>
    
  2. Nel metodo onCreate() dell'attività, imposta un listener di clic 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);
    });

Attiva l'attenuazione a schermo intero

Le attività in genere oscurano il 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 (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.

Estrae un'attività da una suddivisione a una finestra intera

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 suddiviso in fase di runtime

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 di sistema forza le app multiattività in un la 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 in Jetpack WindowManager WindowProperties . 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.

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 una singola 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 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 un nuovo viene avviata dal processo di richiesta, quindi potrebbe non essere a conoscenza dei componenti, che verranno risolti in una fase successiva del processo di sistema quando intent impliciti. Se il nome di un componente non è noto al momento del lancio, viene è possibile utilizzare il carattere jolly ("*/*") e l'applicazione dei filtri per l'azione 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 soltanto nella libreria WindowManager, quando vengono avviate nuove attività con regole corrispondenti e le suddivisioni vengono eliminate quando l'ultima attività in un container di suddivisione completato.
  • Le attività possono essere riavviate quando la configurazione cambia, quindi quando una suddivisione viene creato o rimosso e i limiti di attività cambiano, l'attività può completando la distruzione dell'istanza precedente e la creazione uno nuovo. 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 delle finestre per supportare l'attività incorporamento. Quasi tutti i dispositivi con schermo di grandi dimensioni con Android 12L (livello API 32) o successive includono l'interfaccia. Tuttavia, alcuni dispositivi con schermi grandi non sono in grado di eseguire più attività non includere la finestra l'interfaccia delle estensioni. Se un dispositivo con schermo grande non supporta la modalità multi-finestra potrebbe non supportare l'incorporamento delle attività.

Risorse aggiuntive