L'intégration d'activités permet d'optimiser une application pour les grands écrans en divisant sa fenêtre de tâches en deux activités ou deux instances d'une même activité.
Si votre application se compose de plusieurs activités, l'intégration d'activités vous permet de proposer une expérience utilisateur améliorée sur les tablettes, les pliables et les appareils ChromeOS.
L'intégration d'activités ne nécessite aucune refactorisation du code. Vous déterminez comment votre application affiche ses activités (côte à côte ou empilées) en créant un fichier de configuration XML ou en effectuant des appels d'API Jetpack WindowManager.
Les petits écrans sont gérés automatiquement. Lorsque votre application est installée sur un appareil doté d'un petit écran, les activités sont empilées les unes au-dessus des autres. Sur les grands écrans, les activités sont affichées côte à côte. Le système détermine la présentation en fonction de la configuration que vous avez créée. Aucune logique de ramification n'est nécessaire.
L'intégration d'activités est compatible avec les changements d'orientation des appareils et fonctionne parfaitement sur les appareils pliables. Les activités sont ainsi empilées et dépilées selon que l'appareil est replié ou déplié.
L'intégration d'activités est compatible avec la plupart des appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure.
Fractionner la fenêtre de tâches
L'intégration d'activités divise la fenêtre de tâches de l'application en deux conteneurs : un conteneur principal et un conteneur secondaire. Ils hébergent les activités lancées à partir de l'activité principale ou d'autres activités qui se trouvent déjà dans les conteneurs.
Les activités sont empilées dans le conteneur secondaire au fur et à mesure de leur lancement. Sur les petits écrans, ce conteneur est également empilé sur le conteneur principal. Dans ce cas, l'empilement des activités et la navigation arrière sont cohérents avec l'ordre des activités déjà intégrées dans l'application.
L'intégration d'activités vous permet d'afficher les activités de différentes manières. Votre application peut lancer deux activités côte à côte et fractionner ainsi la fenêtre de tâches:
De même, une activité qui occupe la totalité de la fenêtre de tâches peut également lancer une nouvelle activité en parallèle et fractionner l'écran :
Les activités partageant une fenêtre de tâches et qui sont déjà sur un écran fractionné peuvent lancer d'autres activités de différentes manières :
Sur le côté en haut d'une autre activité :
Sur le côté, en déplaçant horizontalement l'écran fractionné et en masquant ainsi l'activité principale précédente:
La nouvelle activité lancée remplace l'activité lancée précédemment dans la même pile d'activités :
L'activité lancée s'affiche dans toute la fenêtre dans la même tâche :
Navigation vers l'arrière
Différents types d'applications peuvent avoir des règles de navigation arrière différentes lorsque la fenêtre de tâches est fractionnée. Ces règles varient en fonction des dépendances entre les activités ou de la manière dont les utilisateurs déclenchent l'événement "Retour". Voici quelques exemples:
- Fermeture simultanée des activités: si les activités sont liées et ne peuvent pas s'afficher sans l'autre, la navigation arrière peut être configurée pour les fermer toutes les deux.
- Fermeture indépendante d'une activité : si les activités sont totalement indépendantes, la navigation arrière au niveau d'une activité n'affecte pas l'état d'une autre activité dans la fenêtre de tâches.
Si vous utilisez un bouton, l'événement "Retour" est envoyé à la dernière activité sélectionnée.
Pour la navigation par gestes:
Android 14 (niveau d'API 34) ou version antérieure : l'événement "Retour" est envoyé à l'activité où le geste a eu lieu. Lorsque les utilisateurs balaient l'écran depuis le côté gauche, l'événement "Retour" est envoyé à l'activité dans le volet de gauche de la fenêtre fractionnée. Lorsque les utilisateurs balayent l'écran depuis le côté droit, l'événement "Retour" est envoyé à l'activité dans le volet de droite.
Android 15 (niveau d'API 35) ou version ultérieure
Lorsque vous travaillez avec plusieurs activités de la même application, le geste arrête l'activité principale, quelle que soit la direction du balayage, offrant ainsi une expérience plus unifiée.
Dans les scénarios impliquant deux activités provenant de différentes applications (superposition), l'événement "Retour" est dirigé vers la dernière activité sélectionnée, ce qui correspond au comportement de la navigation par bouton.
Mise en page à plusieurs volets
Jetpack WindowManager vous permet de créer une mise en page à plusieurs volets pour intégrer les activités sur les appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure, et sur certains appareils dotés d'une version antérieure de la plate-forme. Les applications existantes qui sont basées sur plusieurs activités plutôt que sur des fragments ou des mises en page basées sur les vues, comme SlidingPaneLayout
, peuvent proposer une meilleure expérience utilisateur sur les grands écrans sans avoir à refactoriser le code source.
Le fractionnement "liste/détail" est un exemple courant. Pour garantir une présentation de qualité, le système lance l'activité "Liste", puis l'application lance immédiatement l'activité "Détail". Le système de transition attend que les deux activités soient dessinées, puis les affiche ensemble. Pour l'utilisateur, ces deux activités sont lancées comme s'il n'en avait qu'une seule.
Attributs de fractionnement
Vous pouvez spécifier les proportions des différents conteneurs fractionnés dans la fenêtre de tâches, ainsi que la façon dont ils seront disposés les uns par rapport aux autres.
Pour les règles définies dans un fichier de configuration XML, définissez les attributs suivants :
splitRatio
: définit les proportions des conteneurs. Cette valeur est un nombre à virgule flottante dans l'intervalle ouvert (0,0, 1,0).splitLayoutDirection
: spécifie la mise en page des conteneurs fractionnés. Voici quelques valeurs possibles :ltr
: de gauche à droite.rtl
: de droite à gauche.locale
: la valeurltr
ourtl
est déterminée à partir des paramètres régionaux.
Pour obtenir des exemples, consultez la section Configuration XML.
Pour les règles créées à l'aide des API WindowManager, créez un objet SplitAttributes
avec SplitAttributes.Builder
et appelez les méthodes de compilateur suivantes:
setSplitType()
: définit les proportions des conteneurs fractionnés. ConsultezSplitAttributes.SplitType
pour connaître les arguments valides, y compris la méthodeSplitAttributes.SplitType.ratio()
.setLayoutDirection()
: définit la mise en page des conteneurs. ConsultezSplitAttributes.LayoutDirection
pour connaître les valeurs possibles.
Pour obtenir des exemples, consultez la section API WindowManager.
Espaces réservés
Les activités d'un espace réservé sont des activités secondaires vides qui occupent un espace spécifique dans un fractionnement d'activité. Ils sont destinés à être remplacés par une autre activité comprenant du contenu. Par exemple, l'activité d'un espace réservé peut occuper le conteneur secondaire d'un fractionnement d'activité dans une mise en page Liste/Détail. Dans ce cas, lorsqu'un élément de la liste est sélectionné, une activité contenant les informations détaillées correspondant à cet élément remplace l'espace réservé.
Par défaut, le système n'affiche les espaces réservés que lorsqu'il y a suffisamment d'espace pour un fractionnement d'activité. Les espaces réservés se terminent automatiquement lorsque la largeur ou la hauteur de l'écran devient trop grande ou trop petite pour qu'il soit fractionné. Lorsque l'espace le permet, le système relance l'espace réservé avec un état réinitialisé.
Toutefois, l'attribut stickyPlaceholder
d'une méthode SplitPlaceholderRule
ou setSticky()
pour SplitPlaceholder.Builder
peut remplacer le comportement par défaut. Lorsque l'attribut ou la méthode spécifie la valeur true
, le système affiche l'espace réservé comme étant l'activité la plus élevée dans la fenêtre de tâches lorsque l'écran est redimensionné pour afficher un seul volet à partir d'un écran à deux volets (voir la section Configuration de fractionnement pour consulter un exemple).
Changements de taille de la fenêtre
Lorsque la configuration de l'appareil réduit la largeur de la fenêtre de tâches de sorte qu'elle ne soit pas assez grande pour une mise en page à plusieurs volets (par exemple, lorsqu'un appareil pliable à grand écran passe de la taille d'une tablette à celle d'un téléphone ou que la fenêtre de l'application est redimensionnée en mode multifenêtre), les activités autres que les espaces réservés dans le volet secondaire de la fenêtre de tâches sont empilées au-dessus des activités du volet principal.
Les activités d'un espace réservé ne s'affichent que lorsque la largeur de l'écran est suffisante pour le fractionnement. Sur les petits écrans, l'espace réservé est automatiquement fermé. Lorsque la zone d'affichage redevient suffisamment grande, l'espace réservé est recréé. (voir la section Espaces réservés).
L'empilement d'activités est possible, car WindowManager classe les activités dans le volet secondaire au-dessus des activités du volet principal.
Plusieurs activités dans le volet secondaire
L'activité B lance l'activité C sans indicateur d'intent supplémentaire :
Les activités sont classées dans cet ordre dans la même tâche :
Par conséquent, dans une fenêtre de tâches de petite taille, l'application se limite à une seule activité et positionne C en haut de la pile:
La navigation arrière dans la fenêtre de petite taille vous permet de parcourir les activités empilées les unes sur les autres.
Si la taille de la fenêtre de tâches est agrandie de sorte à pouvoir accueillir plusieurs volets, les activités s'affichent à nouveau côte à côte.
Fractionnements empilés
L'activité B lance l'activité C sur le côté et déplace l'écran fractionné horizontalement :
Les activités sont classées dans cet ordre dans la même tâche :
Dans une fenêtre de tâches de petite taille, l'application se limite à une seule activité et positionne C en haut:
Mode portrait fixe
Le paramètre de manifeste android:screenOrientation permet aux applications de contraindre les activités au mode portrait ou paysage. Pour améliorer l'expérience utilisateur sur les grands écrans (tablettes et pliables, par exemple), les fabricants d'appareils (OEM) peuvent ignorer les demandes d'orientation de l'écran et encadrer l'application en mode portrait sur les écrans en mode paysage au format letterbox, ou inversement.
De même, lorsque l'intégration d'activités est activée, les OEM peuvent personnaliser les appareils pour encadrer au format letterbox les activités en mode portrait fixe sur les grands écrans (largeur supérieure ou égale à 600 dp) en mode paysage. Lorsqu'une activité de portrait fixe démarre une deuxième activité, l'appareil peut afficher les deux activités côte à côte dans une présentation à deux volets.
Ajoutez toujours la propriété android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
au fichier manifeste de votre application pour indiquer aux appareils que cette dernière est compatible avec l'intégration d'activités (voir la section Configuration de fractionnement). Les appareils personnalisés par l'OEM peuvent ensuite déterminer s'il est nécessaire d'utiliser le format letterbox pour les activités en mode portrait.
Configuration de fractionnement
Les règles de fractionnement permettent de configurer les divisions d'activité. Vous définissez les règles de fractionnement dans un fichier de configuration XML ou en effectuant des appels à l'API Jetpack WindowManager.
Dans les deux cas, votre application doit accéder à la bibliothèque WindowManager et informer le système qu'elle a implémenté l'intégration d'activités.
Procédez comme suit :
Ajoutez la dernière dépendance de la bibliothèque WindowManager au fichier
build.gradle
au niveau du module de votre application, par exemple:implementation 'androidx.window:window:1.1.0-beta02'
La bibliothèque WindowManager fournit tous les composants requis pour l'intégration d'activités.
Informez le système que votre application a implémenté l'intégration d'activités.
Ajoutez la propriété
android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED
à l'élément <application> du fichier manifeste de l'application, puis définissez la valeur sur "true", par exemple:<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>
À partir de la version 1.1.0-alpha06 de WindowManager, les fractionnements de l'intégration d'activités sont désactivés, sauf si la propriété est ajoutée au fichier manifeste et définie sur "true".
En outre, les fabricants d'appareils utilisent ce paramètre afin d'activer les fonctionnalités personnalisées pour les applications compatibles avec l'intégration d'activités. Par exemple, les appareils peuvent utiliser le format letterbox pour une activité en mode portrait sur les affichages en mode paysage. L'activité sera ainsi orientée de sorte à passer à une mise en page à deux volets lorsqu'une deuxième activité commencera (voir Mode portrait fixe).
Configuration XML
Pour créer une implémentation XML de l'intégration d'activités, procédez comme suit:
Créez un fichier de ressources XML qui :
- définit les activités qui partagent un fractionnement ;
- Configure les options de fractionnement.
- Crée un espace réservé pour le conteneur secondaire du fractionnement lorsque le contenu n'est pas disponible.
- Spécifie les activités qui ne doivent jamais faire partie d'un fractionnement.
Exemple :
<!-- 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>
Créez un initialiseur.
Le composant
RuleController
de WindowManager analyse le fichier de configuration XML et met les règles à disposition du système. Une bibliothèque Jetpack StartupInitializer
met le fichier XML à disposition deRuleController
au démarrage de l'application, afin que les règles s'appliquent au démarrage d'une activité.Pour créer un initialiseur, procédez comme suit :
Ajoutez la dernière dépendance de la bibliothèque Jetpack Startup à votre fichier
build.gradle
au niveau du module, par exemple:implementation 'androidx.startup:startup-runtime:1.1.1'
Créez une classe qui implémente l'interface
Initializer
.L'initialiseur met les règles de fractionnement à disposition de
RuleController
en transmettant l'ID du fichier de configuration XML (main_split_config.xml
) à la méthodeRuleController.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(); } }
Créez un fournisseur de contenu pour les définitions de règles.
Ajoutez
androidx.startup.InitializationProvider
au fichier manifeste de votre application en tant que<provider>
. Incluez une référence à l'implémentation de l'initialiseurRuleController
,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
détecte et initialiseSplitInitializer
avant d'appeler la méthodeonCreate()
de l'application. Par conséquent, les règles de fractionnement sont appliquées lorsque l'activité principale de l'application commence.
API WindowManager
Vous pouvez implémenter l'intégration d'une activité de manière programmatique avec quelques appels d'API. Effectuez les appels dans la méthode onCreate()
d'une sous-classe de Application
pour vous assurer que les règles sont en vigueur avant le lancement des activités.
Pour créer un fractionnement des activités de manière programmatique, procédez comme suit :
Créez une règle de fractionnement :
Créez un
SplitPairFilter
qui identifie les activités qui partagent le fractionnement: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 );
Ajoutez le filtre à un ensemble de filtres :
Kotlin
val filterSet = setOf(splitPairFilter)
Java
Set<SplitPairFilter> filterSet = new HashSet<>(); filterSet.add(splitPairFilter);
Créez des attributs de mise en page pour le fractionnement :
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
crée un objet contenant des attributs de mise en page:setSplitType()
: définit la manière dont la zone d'affichage disponible est allouée à chaque conteneur d'activité. Le type de fractionnement des proportions spécifie la proportion de la zone d'affichage disponible allouée au conteneur principal. Le conteneur secondaire occupe le reste de la zone d'affichage.setLayoutDirection()
: spécifie la disposition des conteneurs d'activités les uns par rapport aux autres (le conteneur principal en premier).
Créez une
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
crée et configure la règle :filterSet
: contient des filtres de paire de fractionnement qui déterminent quand appliquer la règle en identifiant les activités qui partagent un fractionnement.setDefaultSplitAttributes()
: applique des attributs de mise en page à la règle.setMinWidthDp()
: définit la largeur d'affichage minimale (en pixels indépendants de la densité, dp) pour permettre le fractionnement.setMinSmallestWidthDp()
: définit la valeur minimale (en dp) que la plus petite des deux dimensions d'affichage doit avoir pour permettre un fractionnement, quelle que soit l'orientation de l'appareil.setMaxAspectRatioInPortrait()
: définit le format maximal d'affichage (height:width) en mode portrait pour lequel les fractionnements d'activité seront affichés. Si le format d'un écran en mode portrait dépasse le format maximal, les écrans fractionnés sont désactivés, quelle que soit la largeur de l'écran. Remarque:La valeur par défaut est 1, 4.Les activités occupent donc toute la fenêtre de tâches en mode portrait sur la plupart des tablettes. Voir égalementSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
etsetMaxAspectRatioInLandscape()
. La valeur par défaut pour le mode paysage estALWAYS_ALLOW
.setFinishPrimaryWithSecondary()
: définit l'impact de toutes les activités effectuées dans le conteneur secondaire sur les activités du conteneur principal.NEVER
indique que le système ne doit pas terminer les activités principales lorsque toutes les activités du conteneur secondaire sont finalisées (voir la section Arrêter les activités).setFinishSecondaryWithPrimary()
: définit l'impact de toutes les activités effectuées dans le conteneur principal sur les activités du conteneur secondaire.ALWAYS
indique que le système doit toujours terminer les activités du conteneur secondaire lorsque toutes les activités du conteneur principal sont finalisées (voir la section Arrêter les activités).setClearTop()
: indique si toutes les activités dans le conteneur secondaire sont arrêtées lorsqu'une nouvelle activité est lancée dans le conteneur. La valeurfalse
indique que les nouvelles activités sont empilées sur les activités déjà présentes dans le conteneur secondaire.
Obtenez l'instance du singleton du
RuleController
WindowManager, puis ajoutez la règle:Kotlin
val ruleController = RuleController.getInstance(this) ruleController.addRule(splitPairRule)
Java
RuleController ruleController = RuleController.getInstance(this); ruleController.addRule(splitPairRule);
Créez un espace réservé pour le conteneur secondaire lorsque le contenu n'est pas disponible:
Créez un
ActivityFilter
qui identifie l'activité avec laquelle l'espace réservé partage un fractionnement de fenêtre de tâches:Kotlin
val placeholderActivityFilter = ActivityFilter( ComponentName(this, ListActivity::class.java), null )
Java
ActivityFilter placeholderActivityFilter = new ActivityFilter( new ComponentName(this, ListActivity.class), null );
Ajoutez le filtre à un ensemble de filtres :
Kotlin
val placeholderActivityFilterSet = setOf(placeholderActivityFilter)
Java
Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>(); placeholderActivityFilterSet.add(placeholderActivityFilter);
Créez 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
crée et configure la règle :placeholderActivityFilterSet
: contient des filtres d'activité qui déterminent quand appliquer la règle en identifiant les activités auxquelles l'activité d'espace réservé est associée.Intent
: spécifie le lancement de l'activité d'espace réservé.setDefaultSplitAttributes()
: applique des attributs de mise en page à la règle.setMinWidthDp()
: définit la largeur d'affichage minimale (en pixels indépendants de la densité, dp) pour permettre le fractionnement.setMinSmallestWidthDp()
: définit la valeur minimale (en dp) que la plus petite des deux dimensions d'affichage doit avoir pour permettre un fractionnement, quelle que soit l'orientation de l'appareil.setMaxAspectRatioInPortrait()
: définit le format maximal d'affichage (height:width) en mode portrait pour lequel les fractionnements d'activité seront affichés. Remarque:La valeur par défaut est 1, 4.Les activités occupent donc toute la fenêtre de tâches en mode portrait sur la plupart des tablettes. Voir égalementSPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT
etsetMaxAspectRatioInLandscape()
. La valeur par défaut pour le mode paysage estALWAYS_ALLOW
.setFinishPrimaryWithPlaceholder()
: définit l'impact de la finalisation de l'activité liée à un espace réservé sur les activités du conteneur principal. "ALWAYS" (TOUJOURS) indique que le système doit toujours terminer les activités dans le conteneur principal lorsque l'espace réservé est finalisé (voir la section Arrêter les activités).setSticky()
: détermine si l'activité d'espace réservé s'affiche en haut de la pile d'activités sur les petits écrans une fois qu'il apparaît pour la première fois dans un écran fractionné avec une largeur minimale suffisante.
Ajoutez la règle à la fenêtre
RuleController
de WindowManager :Kotlin
ruleController.addRule(splitPlaceholderRule)
Java
ruleController.addRule(splitPlaceholderRule);
Spécifiez les activités qui ne doivent jamais faire partie d'un fractionnement :
Créez un
ActivityFilter
qui identifie une activité qui doit toujours occuper la totalité de la zone d'affichage des tâches:Kotlin
val expandedActivityFilter = ActivityFilter( ComponentName(this, ExpandedActivity::class.java), null )
Java
ActivityFilter expandedActivityFilter = new ActivityFilter( new ComponentName(this, ExpandedActivity.class), null );
Ajoutez le filtre à un ensemble de filtres :
Kotlin
val expandedActivityFilterSet = setOf(expandedActivityFilter)
Java
Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>(); expandedActivityFilterSet.add(expandedActivityFilter);
Créez une
ActivityRule
:Kotlin
val activityRule = ActivityRule.Builder(expandedActivityFilterSet) .setAlwaysExpand(true) .build()
Java
ActivityRule activityRule = new ActivityRule.Builder( expandedActivityFilterSet ).setAlwaysExpand(true) .build();
ActivityRule.Builder
crée et configure la règle :expandedActivityFilterSet
: contient les filtres d'activité qui déterminent quand appliquer la règle en identifiant les activités que vous souhaitez exclure des écrans fractionnés.setAlwaysExpand()
: indique si l'activité doit remplir toute la fenêtre de tâches.
Ajoutez la règle à la fenêtre
RuleController
de WindowManager :Kotlin
ruleController.addRule(activityRule)
Java
ruleController.addRule(activityRule);
Intégration entre les applications
Sur Android 13 (niveau d'API 33) ou version ultérieure, les applications peuvent intégrer des activités provenant d'autres applications. L'intégration d'activités entre les applications, également appelée UID, permet de rassembler visuellement les activités de plusieurs applications Android. Le système affiche à l'écran une activité de l'application hôte et une activité intégrée provenant d'une autre application côte-à-côte ou en haut et en bas, comme pour l'intégration d'activités dans une application unique.
Par exemple, l'application Paramètres peut intégrer l'activité de sélecteur de fond d'écran à partir de l'application WallpaperPicker:
Modèle de confiance
Les processus hôtes qui intègrent des activités provenant d'autres applications sont en mesure de redéfinir la présentation des activités intégrées, y compris la taille, la position, le recadrage et la transparence. Des hôtes malveillants peuvent exploiter cette fonctionnalité pour induire les utilisateurs en erreur et créer des attaques de détournement de clic ou d'autres tentatives de redressement de l'UI.
Pour éviter l'usage abusif de l'intégration d'activités entre les applications, Android demande aux applications d'autoriser l'intégration de leurs activités. Elles peuvent désigner des hôtes approuvés ou non.
Hôtes approuvés
Pour permettre à d'autres applications d'intégrer et de contrôler totalement la présentation des activités à partir de votre application, spécifiez le certificat SHA-256 de l'application hôte dans l'attribut android:knownActivityEmbeddingCerts
des éléments <activity>
ou <application>
de son fichier manifeste.
Définissez la valeur de android:knownActivityEmbeddingCerts
sous forme de chaîne :
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
... />
ou, pour spécifier plusieurs certificats, un tableau de chaînes :
<activity
android:name=".MyEmbeddableActivity"
android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
... />
qui fait référence à une ressource semblable à celle-ci :
<resources>
<string-array name="known_host_certificate_digests">
<item>cert1</item>
<item>cert2</item>
...
</string-array>
</resources>
Les propriétaires d'applications peuvent obtenir un condensé de certificat SHA en exécutant la tâche signingReport
Gradle. Le condensé du certificat correspond à l'empreinte SHA-256, sans les deux-points de séparation. Pour en savoir plus, consultez Générer un rapport de signature et Authentifier votre client.
Hôtes non approuvés
Pour autoriser n'importe quelle application à intégrer les activités de votre application et à contrôler leur présentation, spécifiez l'attribut android:allowUntrustedActivityEmbedding
dans les éléments <activity>
ou <application>
du fichier manifeste de l'application, par exemple:
<activity
android:name=".MyEmbeddableActivity"
android:allowUntrustedActivityEmbedding="true"
... />
La valeur par défaut de cet attribut est "false", ce qui empêche l'intégration d'activités entre les applications.
Authentification personnalisée
Pour atténuer les risques liés à l'intégration d'activités non approuvées, créez un mécanisme d'authentification personnalisé qui valide l'identité de l'hôte. Si vous connaissez les certificats hôtes, utilisez la bibliothèque androidx.security.app.authenticator
pour vous authentifier. Si l'hôte s'authentifie après l'intégration de votre activité, vous pouvez afficher le contenu réel. Sinon, vous pouvez informer l'utilisateur que l'action n'a pas été autorisée et bloquer le contenu.
Utilisez la méthode ActivityEmbeddingController#isActivityEmbedded()
de la bibliothèque Jetpack WindowManager pour vérifier si un hôte intègre votre activité, par exemple:
Kotlin
fun isActivityEmbedded(activity: Activity): Boolean { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity) }
Java
boolean isActivityEmbedded(Activity activity) { return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity); }
Restriction liée à la taille minimale
Le système Android applique la hauteur et la largeur minimales spécifiées dans l'élément <layout>
du fichier manifeste de l'application aux activités intégrées. Si une application ne spécifie pas de hauteur ni de largeur minimales, les valeurs par défaut du système s'appliquent (sw220dp
).
Si l'hôte tente de redimensionner le conteneur intégré en dessous de la valeur minimale, le conteneur intégré se développe pour occuper toutes les limites de la tâche.
<activity-alias>
Pour que l'intégration d'activités approuvées ou non fonctionne avec l'élément <activity-alias>
, android:knownActivityEmbeddingCerts
ou android:allowUntrustedActivityEmbedding
doit être appliqué à l'activité cible plutôt qu'à l'alias. La règle qui confirme la sécurité sur le serveur système est basée sur les indicateurs définis sur la cible, et non sur l'alias.
Application hôte
Les applications hôtes implémentent l'intégration d'activités entre les applications de la même manière que l'intégration d'activités dans une seule application. Les objets SplitPairRule
et SplitPairFilter
ou ActivityRule
et ActivityFilter
spécifient l'intégration activités et le fractionnement de la fenêtre de tâches. Les règles de fractionnement sont définies de manière statique au format XML ou au moment de l'exécution à l'aide d'appels à l'API Jetpack WindowManager.
Si une application hôte tente d'intégrer une activité pour laquelle l'intégration entre les applications n'a pas été activée, l'activité occupe l'intégralité des limites de la tâche. Par conséquent, les applications hôtes doivent savoir si les activités cibles autorisent l'intégration entre les applications ou non.
Si une activité intégrée lance une nouvelle activité dans la même tâche et que la nouvelle activité ne permet pas l'intégration entre les applications, elle occupe la totalité des limites de la tâche au lieu d'être superposée à l'activité dans le conteneur intégré.
Une application hôte peut intégrer ses propres activités sans restriction, à condition qu'elles soient lancées dans la même tâche.
Exemples de fractionnement
Fractionnement de la fenêtre entière
Aucune refactorisation n'est nécessaire. Vous pouvez définir la configuration de fractionnement en mode statique ou au moment de l'exécution, puis appeler Context#startActivity()
sans aucun paramètre supplémentaire.
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Fractionnement par défaut
Lorsque la page de destination d'une application est conçue pour être fractionnée en deux conteneurs sur les grands écrans, l'expérience utilisateur est optimale lorsque les deux activités sont créées et présentées simultanément. Toutefois, le contenu destiné au conteneur secondaire du fractionnement n'est pas toujours disponible tant que l'utilisateur n'a pas interagi avec l'activité du conteneur principal (tant qu'il n'a pas sélectionné un élément dans un menu de navigation, par exemple). Une activité d'espace réservé peut combler le vide jusqu'à ce que le contenu puisse être affiché dans le conteneur secondaire du fractionnement (voir la section Espaces réservés).
Pour créer un fractionnement avec un espace réservé, créez un espace réservé et associez-le à l'activité principale:
<SplitPlaceholderRule
window:placeholderActivityName=".PlaceholderActivity">
<ActivityFilter
window:activityName=".MainActivity"/>
</SplitPlaceholderRule>
Fractionnement par lien profond
Lorsqu'une application reçoit un intent, l'activité cible peut être affichée en tant que partie secondaire d'un fractionnement d'activité (requête d'affichage d'un écran détaillé contenant des informations sur l'élément d'une liste, par exemple). Sur les petits écrans, le détail s'affiche dans la fenêtre de tâches entière. Sur les grands écrans, il s'affiche à côté de la liste.
La requête de lancement doit être acheminée vers l'activité principale, et l'activité cible destinée aux détails doit être lancée dans un écran fractionné. Le système choisit automatiquement la présentation appropriée (empilée ou côte à côte) en fonction de la largeur d'affichage disponible.
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 destination du lien profond peut être la seule activité disponible pour l'utilisateur dans la pile de navigation arrière, auquel cas il peut être utile de ne pas fermer l'activité "Détail" pour n'afficher que l'activité principale:
À la place, vous pouvez arrêter les deux activités simultanément avec l'attribut finishPrimaryWithSecondary
:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".ListActivity"
window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>
Consultez la section Attributs de configuration.
Plusieurs activités dans des conteneurs fractionnés
L'empilement de plusieurs activités dans un conteneur fractionné permet aux utilisateurs d'accéder à un contenu profond. Par exemple, avec un fractionnement Liste/Détail, l'utilisateur doit parfois accéder à une section de sous-détails tout en conservant l'activité 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'activité du sous-détail est placée au-dessus de l'activité du détail et la dissimule :
L'utilisateur peut ensuite revenir au niveau de détail précédent via la navigation arrière dans la pile :
Les activités sont empilées par défaut lorsqu'elles sont lancées à partir d'une activité qui se trouve dans le même conteneur secondaire. Les activités lancées à partir du conteneur principal dans un fractionnement actif se retrouvent également dans le conteneur secondaire, au-dessus de la pile d'activités.
Activités dans une nouvelle tâche
Lorsque des activités d'une fenêtre de tâches fractionnée déclenchent d'autres activités dans une nouvelle tâche, cette dernière est distincte de la tâche qui inclut le fractionnement et s'affiche en plein écran. L'écran "Recents" (Éléments récents) affiche deux tâches : la tâche sur l'écran fractionné et la nouvelle tâche.
Remplacement d'activité
Les activités peuvent être remplacées dans la pile du conteneur secondaire (lorsque l'activité principale est utilisée pour la navigation de premier niveau et que l'activité secondaire est une destination sélectionnée, par exemple). Chaque sélection dans la navigation de premier niveau devrait lancer une nouvelle activité dans le conteneur secondaire et supprimer l'activité ou les activités qui s'y trouvaient précédemment.
Si l'application ne termine pas l'activité du conteneur secondaire lorsque la sélection change dans la navigation, la navigation arrière peut être source de confusion lorsque le fractionnement est réduit (lorsque l'appareil est plié). Par exemple, si vous avez un menu dans le volet principal et que les écrans A et B sont empilés dans le volet secondaire, lorsque l'utilisateur plie le téléphone, B se trouvera au-dessus de l'écran A, qui se trouvera lui-même au-dessus du menu. Lorsque l'utilisateur effectuera un retour arrière à partir de B, l'écran A apparaîtra au lieu du menu.
Dans ce cas, l'écran A doit être supprimé de la pile "Retour".
Par défaut, le lancement sur le côté dans un nouveau conteneur au-dessus d'un écran déjà fractionné place les nouveaux conteneurs secondaires au-dessus et conserve les anciens dans la pile "Retour". Vous pouvez configurer les fractionnements pour que les conteneurs secondaires précédents soient effacés avec clearTop
et lancer normalement les nouvelles activités.
<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))); } }
Vous pouvez également utiliser la même activité secondaire et, à partir de l'activité principale (menu), envoyer de nouveaux intents qui renvoient à la même instance, mais déclenchent une mise à jour de l'état ou de l'interface utilisateur dans le conteneur secondaire.
Fonctionnement de plusieurs écrans fractionnés
Les applications peuvent fournir une navigation profonde à plusieurs niveaux en lançant des activités supplémentaires sur le côté.
Lorsqu'une activité dans un conteneur secondaire lance une nouvelle activité sur le côté, un écran fractionné est créé au-dessus de l'écran fractionné existant.
La pile "Retour" contient toutes les activités qui ont été ouvertes afin que les utilisateurs puissent accéder à l'écran fractionné A/B une fois qu'ils auront fermé l'écran C.
Pour créer un autre écran fractionné, lancez la nouvelle activité sur le côté du conteneur secondaire existant. Déclarez les configurations correspondant aux écrans fractionnés A/B et B/C, puis lancez l'activité C normalement à partir de 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)); } }
Réagir aux changements d'état des écrans fractionnés
Différentes activités d'application peuvent comporter des éléments d'interface utilisateur ayant la même fonction (commande qui ouvre une fenêtre contenant les paramètres du compte, par exemple).
Si deux activités qui partagent un élément d'interface utilisateur commun sont fractionnées, il est redondant et parfois déroutant d'afficher cet élément dans les deux activités.
Pour déterminer quand les activités sont dans un écran fractionné, consultez le flux SplitController.splitInfoList
ou enregistrez un écouteur avec SplitControllerCallbackAdapter
afin de suivre les changements d'état des écrans fractionnés. Ajustez ensuite l'interface utilisateur en conséquence:
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); }); }
Les coroutines peuvent être lancées à n'importe quel état du cycle de vie, mais elles sont généralement lancées à l'état STARTED
pour préserver les ressources (voir la section Utiliser des coroutines Kotlin avec des composants tenant compte du cycle de vie pour en savoir plus).
Les rappels peuvent être effectués à n'importe quel état du cycle de vie, y compris lorsqu'une activité est arrêtée. L'enregistrement des écouteurs doit généralement être activé dans onStart()
et être annulé dans onStop()
.
Fenêtre modale plein écran
Certaines activités empêchent les utilisateurs d'interagir avec l'application tant qu'une action spécifique n'a pas été effectuée (par exemple, un écran d'authentification, un écran de confirmation d'un règlement ou un message d'erreur). Les activités modales ne doivent pas s'afficher dans des écrans fractionnés.
Pour forcer une activité à toujours remplir la fenêtre de tâches, utilisez la configuration d'expansion :
<ActivityRule
window:alwaysExpand="true">
<ActivityFilter
window:activityName=".FullWidthActivity"/>
</ActivityRule>
Arrêter les activités
Les utilisateurs peuvent arrêter les activités de chaque côté de l'écran fractionné en balayant l'écran depuis le bord:
Si l'appareil est configuré pour utiliser le bouton "Retour" au lieu de la navigation par gestes, l'entrée est envoyée à l'activité sélectionnée, à savoir celle sur laquelle l'utilisateur a appuyé en dernier ou celle qu'il a lancée en dernier.
L'impact de la finalisation de toutes les activités d'un conteneur sur le conteneur opposé dépend de la configuration du fractionnement.
Attributs de configuration
Vous pouvez spécifier des attributs de règle pour la paire fractionnée afin de configurer l'impact de toutes les activités effectuées d'un côté sur les activités qui se trouvent de l'autre côté du fractionnement. Les attributs sont les suivants :
window:finishPrimaryWithSecondary
: impact de toutes les activités effectuées dans le conteneur secondaire sur les activités du conteneur principalwindow:finishSecondaryWithPrimary
: impact de toutes les activités effectuées dans le conteneur principal sur les activités du conteneur secondaire
Les valeurs possibles des attributs sont les suivantes :
always
: toujours terminer les activités dans le conteneur associénever
: ne jamais terminer les activités dans le conteneur associéadjacent
: terminer les activités dans le conteneur associé lorsque les deux conteneurs sont affichés l'un à côté de l'autre, mais pas lorsqu'ils sont empilés l'un au-dessus de l'autre
Exemple :
<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>
Configuration par défaut
Lorsque toutes les activités d'un conteneur sont finalisées, le conteneur restant occupe la totalité de la fenêtre:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Mettre fin aux activités simultanément
Pour mettre fin automatiquement aux activités du conteneur principal lorsque toutes les activités du conteneur secondaire sont finalisées:
<SplitPairRule
window:finishPrimaryWithSecondary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Pour mettre fin automatiquement aux activités du conteneur secondaire lorsque toutes les activités du conteneur principal sont finalisées:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Pour mettre fin aux activités simultanément lorsque toutes les activités du conteneur principal ou secondaire sont finalisées:
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Mettre fin à plusieurs activités dans les conteneurs
Si plusieurs activités sont empilées dans un conteneur fractionné, l'arrêt d'une activité au bas de la pile ne met pas automatiquement fin aux activités en haut de la pile.
Par exemple, si deux activités se trouvent dans le conteneur secondaire, C au-dessus de B :
Et si la configuration du fractionnement est définie par la configuration des activités A et B:
<SplitPairRule>
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
L'arrêt de l'activité située en haut de la pile conserve le fractionnement.
L'arrêt de l'activité inférieure (racine) du conteneur secondaire n'entraîne pas la suppression des activités qui se trouvent au-dessus, et conserve donc aussi le fractionnement.
Toute règle supplémentaire permettant de mettre fin aux activités simultanément, telle que l'arrêt de l'activité secondaire en même temps que l'activité principale, est également exécutée:
<SplitPairRule
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Et lorsque le fractionnement est configuré pour mettre fin à l'activité principale et à l'activité secondaire simultanément :
<SplitPairRule
window:finishPrimaryWithSecondary="always"
window:finishSecondaryWithPrimary="always">
<SplitPairFilter
window:primaryActivityName=".A"
window:secondaryActivityName=".B"/>
</SplitPairRule>
Modifier les propriétés de fractionnement au moment de l'exécution
Il n'est pas possible de modifier les propriétés d'un fractionnement actif et visible. La modification des règles de fractionnement s'applique au lancement d'activités supplémentaires et aux nouveaux conteneurs, pas aux fractionnements actifs existants.
Pour modifier les propriétés des fractionnements actifs, arrêtez les activités secondaires dans l'écran fractionné, puis lancez-les de nouveau sur le côté avec une nouvelle configuration.
Propriétés de fractionnement dynamique
Android 15 (niveau d'API 35) ou version ultérieure, compatible avec Jetpack WindowManager 1.4 ou version ultérieure, offre des fonctionnalités dynamiques qui permettent de configurer les fractionnements d'intégration d'activités, y compris:
- Élargissement des volets:un séparateur interactif et déplaçable permet aux utilisateurs de redimensionner les volets dans une présentation fractionnée.
- Épinglage de la pile d'activités:les utilisateurs peuvent épingler le contenu dans un conteneur et isoler la navigation dans ce conteneur de celle dans l'autre.
- Atténuation de la luminosité des boîtes de dialogue en plein écran:lorsque vous affichez une boîte de dialogue, les applications peuvent spécifier si elles doivent atténuer la luminosité de l'intégralité de la fenêtre de tâche ou uniquement du conteneur qui a ouvert la boîte de dialogue.
Expansion des volets
L'expansion des volets permet aux utilisateurs d'ajuster l'espace d'écran alloué aux deux activités dans une mise en page à double volet.
Pour personnaliser l'apparence du séparateur de fenêtre et définir la plage de glissement du séparateur, procédez comme suit:
Créer une instance de
DividerAttributes
Personnalisez les attributs du séparateur:
color
:couleur du séparateur de volets déplaçable.widthDp
:largeur du séparateur de volets déplaçable. Définissez la valeur surWIDTH_SYSTEM_DEFAULT
pour laisser le système déterminer la largeur du séparateur.Plage de glissement:pourcentage minimal de l'écran que chaque volet peut occuper. Peut varier de 0,33 à 0,66. Définissez la valeur sur
DRAG_RANGE_SYSTEM_DEFAULT
pour laisser le système déterminer la plage de glissement.
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();
Épinglage de la pile d'activités
L'épinglage de la pile d'activités permet aux utilisateurs d'épingler l'une des fenêtres fractionnées afin que l'activité reste telle quelle pendant que les utilisateurs naviguent dans l'autre fenêtre. L'épinglage de la pile d'activités offre une expérience multitâche améliorée.
Pour activer l'épinglage de la pile d'activités dans votre application, procédez comme suit:
Ajoutez un bouton au fichier de mise en page de l'activité que vous souhaitez épingler, par exemple l'activité "Détail" d'une mise en page Liste/Détail:
<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>
Dans la méthode
onCreate()
de l'activité, définissez un écouteur onclick sur le bouton: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); });
Diminution de la luminosité des boîtes de dialogue en plein écran
Les activités atténuent généralement leur écran pour attirer l'attention sur une boîte de dialogue. Dans l'intégration d'activités, les deux volets de l'écran à deux volets doivent s'assombrir, et non seulement le volet contenant l'activité qui a ouvert la boîte de dialogue, pour une expérience d'UI unifiée.
Avec WindowManager 1.4 ou version ultérieure, l'intégralité de la fenêtre de l'application est atténuée par défaut lorsqu'une boîte de dialogue s'ouvre (voir EmbeddingConfiguration.DimAreaBehavior.ON_TASK
).
Pour diminuer uniquement la luminosité du conteneur de l'activité qui a ouvert la boîte de dialogue, utilisez EmbeddingConfiguration.DimAreaBehavior.ON_ACTIVITY_STACK
.
Extraire une activité d'un écran fractionné pour l'afficher dans la fenêtre complète
Créez une configuration qui affichera l'activité secondaire dans une fenêtre complète, puis relancez cette activité avec un intent qui résout la même instance.
Vérifier la prise en charge du fractionnement au moment de l'exécution
L'intégration d'activités est compatible avec Android 12L (niveau d'API 32) ou version ultérieure. Elle est également disponible sur certains appareils exécutant des versions de plate-forme antérieures. Pour vérifier la disponibilité de cette fonctionnalité au moment de l'exécution, utilisez la propriété SplitController.splitSupportStatus
ou la méthode 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. }
Si les fractionnements ne sont pas pris en charge, les activités sont lancées au-dessus de la pile d'activités (en suivant le modèle d'intégration hors activité).
Empêcher le remplacement du système
Les fabricants d'appareils Android (fabricants d'équipement d'origine, ou OEM) peuvent implémenter l'intégration d'activités en tant que fonction du système de l'appareil. Le système spécifie des règles de fractionnement pour les applications à plusieurs activités, ignorant ainsi leur comportement de fenêtrage. Le remplacement du système force les applications à plusieurs activités à utiliser un mode d'intégration d'activités défini par le système.
L'intégration des activités du système peut améliorer la présentation de l'appli au moyen de mises en page à plusieurs volets, telle que list-detail, sans modifier l'appli. Toutefois, ce type d'intégration peut également donner lieu à des mises en page d'application incorrectes, des bugs ou des conflits avec l'intégration des activités implémentée par l'appli.
Votre application peut empêcher ou autoriser l'intégration des activités système en définissant une propriété dans son fichier manifeste, par exemple:
<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>
Le nom de la propriété est défini dans l'objet WindowProperties
de Jetpack WindowManager. Définissez la valeur sur false
si votre application implémente l'intégration d'activités ou si vous souhaitez empêcher le système d'appliquer ses règles d'intégration d'activités à votre application. Définissez la valeur sur true
pour autoriser le système à appliquer son intégration d'activités à votre application.
Limites, restrictions et mises en garde
- Seule l'application hôte de la tâche, identifiée comme propriétaire de l'activité racine, peut y organiser et y intégrer d'autres activités. Si les activités compatibles avec l'intégration et le fractionnement s'exécutent dans une tâche appartenant à une autre application, ces opérations ne fonctionneront pas.
- Les activités ne peuvent être organisées que dans une seule tâche. Le lancement d'une activité dans une nouvelle tâche la place toujours dans une nouvelle fenêtre développée en dehors des écrans fractionnés existants.
- Seules les activités d'un même processus peuvent être organisées et fractionnées. Le rappel
SplitInfo
ne signale que les activités qui appartiennent au même processus, car il n'existe aucun moyen d'identifier les activités associées à d'autres processus. - Chaque règle d'activité unique ou par paire s'applique uniquement aux lancements d'activités qui se produisent après l'enregistrement de la règle. Il n'existe actuellement aucun moyen de mettre à jour les fractionnements actifs ni leurs propriétés visuelles.
- La configuration du filtre de paire de fractionnement doit correspondre aux intents utilisés lors du lancement complet des activités. La mise en correspondance intervient au moment où une nouvelle activité est lancée à partir du processus d'application. Il est donc possible qu'elle ne connaisse pas les noms de composants résolus ultérieurement dans le processus système lors de l'utilisation d'intents implicites. Si le nom d'un composant n'est pas connu au moment du lancement, vous pouvez utiliser un caractère générique ("*/*") à la place, et un filtrage basé sur l'action d'intent.
- Il n'existe actuellement aucun moyen de déplacer des activités entre des conteneurs, ni de les ajouter à un écran fractionné ou de les en sortir après leur création. Les écrans fractionnés ne sont créés par la bibliothèque WindowManager que lorsque de nouvelles activités avec des règles associées sont lancées. Les fractionnements sont entièrement éliminés à l'arrêt de la dernière activité dans un écran fractionné.
- Les activités peuvent être redémarrées lorsque la configuration change. Dès lors, lorsqu'un fractionnement est créé ou supprimé et que les limites de l'activité changent, celle-ci peut subir une destruction complète, puis être créée à nouveau. Par conséquent, les développeurs d'applications doivent prêter attention à des comportements spécifiques tels que le lancement de nouvelles activités à partir de rappels de cycle de vie.
- Les appareils doivent inclure l'interface des extensions de fenêtre pour permettre l'intégration d'activités. Presque tous les appareils à grand écran équipés d'Android 12L (niveau d'API 32) ou version ultérieure incluent cette interface. Toutefois, ce n'est pas le cas de certains appareils à grand écran qui ne permettent pas l'exécution de plusieurs activités. Si un appareil à grand écran n'est pas compatible avec le mode multifenêtre, il est possible qu'il ne permette pas l'intégration d'activités.
Ressources supplémentaires
- Ateliers de programmation :
- Parcours de formation : Intégration d'activités
- Application exemple : activity-embedding