Créer une mise en page de type "Liste/Détail" avec l'intégration d'activités et Material Design

1. Introduction

Les grands écrans vous permettent de créer des mises en page et des interfaces utilisateur qui améliorent l'expérience utilisateur et augmentent la productivité. Toutefois, si votre application est conçue pour les petits écrans des téléphones non pliables, elle ne peut probablement pas profiter de l'espace supplémentaire disponible sur les tablettes, les pliables et les appareils ChromeOS.

Mettre à jour une application pour tirer le meilleur parti possible des grands écrans peut prendre beaucoup de temps et s'avérer coûteux, en particulier pour les anciennes applications basées sur plusieurs activités.

L'intégration d'activités, introduite dans Android 12L (niveau d'API 32), permet aux applications basées sur des activités d'afficher plusieurs activités simultanément sur les grands écrans en créant une mise en page à deux volets (Liste/Détail, par exemple). Aucune modification du code Kotlin ou Java n'est nécessaire. Il vous suffit d'ajouter des dépendances, de créer un fichier de configuration XML, d'implémenter un initialiseur et d'ajouter quelques éléments au fichier manifeste de l'application. Si vous préférez manipuler du code, vous pouvez ajouter simplement quelques appels d'API Jetpack WindowManager à la méthode onCreate() de l'activité principale de votre application.

Conditions préalables

Voici les conditions à réunir pour effectuer cet atelier de programmation :

  • Vous savez créer des applications Android.
  • Vous connaissez le fonctionnement des activités.
  • Vous connaissez le langage XML.
  • Vous connaissez Android Studio, y compris la configuration d'appareils virtuels.

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez mettre à jour une application basée sur des activités afin de permettre une mise en page à deux volets semblable à SlidingPaneLayout. Sur les petits écrans, l'application superpose les activités (piles) les unes sur les autres dans la fenêtre de tâches.

Activités A, B et C empilées dans la fenêtre de tâches

Sur les grands écrans, l'application affiche deux activités simultanément à l'écran, côte à côte ou en haut et en bas, en fonction de vos spécifications.

4b27b07b7361d6d8.png

Points abordés

Implémenter l'intégration d'activités de deux façons :

  • Avec un fichier de configuration XML
  • Avec des appels d'API Jetpack WindowManager

Ce dont vous avez besoin

  • Version récente d'Android Studio
  • Téléphone ou émulateur Android
  • Petite tablette ou émulateur Android
  • Grande tablette ou émulateur Android

2. Configuration

Obtenir l'application exemple

Étape 1 : Cloner le dépôt

Clonez le dépôt Git pour les ateliers de programmation spécifiques aux grands écrans :

git clone https://github.com/android/large-screen-codelabs

Vous pouvez aussi télécharger et décompresser le fichier ZIP correspondant aux ateliers de programmation pour les grands écrans :

Étape 2 : Inspecter les fichiers sources de l'atelier de programmation

Accédez au dossier "activity-embedding".

Étape 3 : Ouvrir le projet de l'atelier de programmation

Dans Android Studio, ouvrez le projet Kotlin ou Java.

Liste des fichiers du dossier d'intégration d'activités dans le dépôt et le fichier ZIP.

Le dossier d'intégration d'activités du dépôt et du fichier ZIP contient deux projets Android Studio : un en Kotlin et un en Java. Ouvrez le projet de votre choix. Les extraits de code sont fournis dans les deux langages.

Créer des appareils virtuels

Si vous ne disposez pas d'un téléphone Android, d'une petite tablette ou d'une grande tablette avec un niveau d'API 32 ou supérieur, ouvrez le gestionnaire d'appareils dans Android Studio et créez les appareils virtuels suivants dont vous avez besoin parmi ceux ci-dessous :

  • Téléphone : Pixel 6, niveau d'API 32 ou supérieur
  • Petite tablette : 7 WSVGA (tablette), niveau d'API 32 ou supérieur
  • Grande tablette : Pixel C, niveau d'API 32 ou supérieur

3. Exécuter l'application

L'application exemple affiche une liste d'éléments. Lorsque l'utilisateur sélectionne un de ces éléments, l'application affiche des informations le concernant.

L'application comporte trois activités :

  • ListActivity : contient une liste d'éléments dans une RecyclerView.
  • DetailActivity : affiche les informations sur l'élément sélectionné dans la liste.
  • SummaryActivity : affiche un résumé des informations lorsque l'élément "Summary" (Résumé) est sélectionné dans la liste.

Comportement sans l'intégration d'activités

Exécutez l'application exemple pour voir son comportement sans l'intégration d'activités :

  1. Exécutez l'application exemple sur votre grande tablette ou votre émulateur Pixel C. L'activité principale (à savoir, la liste) s'affiche :

Grande tablette avec une application exemple exécutée en mode portrait Activité "Liste" en plein écran

  1. Sélectionnez un élément de la liste pour lancer une activité secondaire (à savoir, le détail de l'élément sélectionné). L'activité "Détail" se superpose à l'activité "Liste" :

Grande tablette avec une application exemple exécutée en mode portrait Activité "Détail" en plein écran

  1. Faites pivoter la tablette en mode paysage. L'activité secondaire est toujours superposée à l'activité principale et occupe la totalité de l'écran :

Grande tablette avec une application exemple exécutée en mode paysage Activité "Détail" en plein écran

  1. Sélectionnez la commande "Retour" (flèche vers la gauche dans la barre d'application) pour revenir à la liste.
  2. Sélectionnez le dernier élément de la liste, "Summary" (Résumé) pour lancer une activité récapitulative en tant qu'activité secondaire. Cette activité se superpose à l'activité "Liste" :

Grande tablette avec une application exemple exécutée en mode portrait Activité "Summary" (Résumé) en plein écran

  1. Faites pivoter la tablette en mode paysage. L'activité secondaire est toujours superposée à l'activité principale et occupe la totalité de l'écran :

Grande tablette avec une application exemple exécutée en mode paysage Activité "Summary" (Résumé) en plein écran

Comportement avec l'intégration d'activités

Une fois cet atelier de programmation terminé, l'orientation paysage affiche les activités "Liste" et "Détail" côte à côte dans une mise en page de type "Liste/Détail" :

Grande tablette avec une application exemple exécutée en mode paysage Activités "Liste" et "Détail" côte à côte dans une mise en page "Liste/Détail"

Toutefois, vous allez configurer le résumé pour qu'il s'affiche en plein écran, même si l'activité est lancée depuis un écran fractionné. Le résumé se superpose à l'écran fractionné :

Grande tablette avec une application exemple exécutée en mode paysage Activité "Summary" (Résumé) en plein écran

4. Contexte

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. Toute activité peut générer un fractionnement en lançant une autre activité. L'activité initiale occupe alors le conteneur principal, et l'activité lancée le conteneur secondaire.

L'activité principale peut lancer des activités supplémentaires dans le conteneur secondaire. Les activités des deux conteneurs peuvent ensuite lancer des activités dans leurs conteneurs respectifs. Chaque conteneur peut comporter une pile d'activités. Pour en savoir plus, consultez le guide du développeur sur l'intégration d'activités.

Vous allez configurer votre application pour qu'elle accepte l'intégration d'activités en créant un fichier de configuration XML ou en effectuant des appels d'API Jetpack WindowManager. Commençons par l'approche de configuration XML.

5. Configuration XML

Les conteneurs et les fractionnements liés à l'intégration d'activités sont créés et gérés par la bibliothèque Jetpack WindowManager. Ils reposent sur des règles de fractionnement que vous créez dans un fichier de configuration XML.

Ajouter la dépendance WindowManager

Autorisez l'application exemple à accéder à la bibliothèque WindowManager en ajoutant la dépendance de la bibliothèque au fichier build.gradle au niveau du module de l'application. Exemple :

build.gradle

 implementation 'androidx.window:window:1.2.0'

Informer le système

Indiquez au 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 sa valeur sur "true" :

AndroidManifest.xml

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

Ce paramètre permet aux fabricants d'appareils (OEM) d'activer des 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 (voir android:screenOrientation). L'activité sera ainsi orientée de sorte à passer à une mise en page à deux volets :

Intégration d'activités avec une application en mode portrait sur un affichage en mode paysage L'activité A au format letterbox, en mode portrait uniquement, lance l'activité intégrée B.

Créer un fichier de configuration

Créez un fichier de ressources XML nommé main_split_config.xml dans le dossier res/xml de votre application avec l'élément racine resources.

Remplacez l'espace de noms XML par :

main_split_config.xml

xmlns:window="http://schemas.android.com/apk/res-auto"

Règle de paire de fractionnement

Ajoutez la règle de fractionnement suivante au fichier de configuration :

main_split_config.xml

<!-- Define a split for the named activity pair. -->
<SplitPairRule
    window:splitRatio="0.33"
    window:splitMinWidthDp="840"
    window:finishPrimaryWithSecondary="never"
    window:finishSecondaryWithPrimary="always">
  <SplitPairFilter
      window:primaryActivityName=".ListActivity"
      window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

Cette règle a l'effet suivant :

  • Configure les options de fractionnement pour les activités qui partagent un écran :
  • splitRatio : indique la partie de la fenêtre de tâches occupée par l'activité principale (33 %), ce qui laisse l'espace restant à l'activité secondaire.
  • splitMinWidthDp : spécifie la largeur minimale (840) requise pour que les deux activités s'affichent simultanément à l'écran. Les unités sont des pixels indépendants de l'affichage (dp).
  • finishPrimaryWithSecondary : spécifie si les activités du conteneur fractionné principal se terminent (ou ne se terminent jamais) lorsque toutes les activités du conteneur secondaire sont finalisées.
  • finishSecondaryWithPrimary : spécifie si les activités du conteneur fractionné secondaire se terminent (toujours) lorsque toutes les activités de l'activité du conteneur principal sont finalisées.
  • Inclut un filtre de fractionnement qui définit les activités qui partagent un fractionnement de fenêtre de tâches. L'activité principale est ListActivity. DetailActivity est l'activité secondaire.

Règle d'espace réservé

Une activité d'espace réservé occupe le conteneur secondaire d'un fractionnement d'activité lorsqu'aucun contenu n'est disponible pour ce conteneur spécifique, par exemple lorsqu'un fractionnement liste/détails s'ouvre, mais qu'aucun élément n'a encore été sélectionné dans la liste. Pour en savoir plus, consultez la section Espaces réservés dans le guide du développeur sur l'intégration d'activités.

Ajoutez la règle d'espace réservé suivante au fichier de configuration :

main_split_config.xml

<!-- Automatically launch a placeholder for the detail activity. -->
<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity"
    window:splitRatio="0.33"
    window:splitMinWidthDp="840"
    window:finishPrimaryWithPlaceholder="always"
    window:stickyPlaceholder="false">
  <ActivityFilter
      window:activityName=".ListActivity"/>
</SplitPlaceholderRule>

Cette règle a l'effet suivant :

  • Identifie l'activité d'espace réservé, PlaceholderActivity (nous la créerons à l'étape suivante).
  • Configure les options de l'espace réservé :
  • splitRatio : indique la partie de la fenêtre de tâches occupée par l'activité principale (33 %), ce qui laisse l'espace restant à l'espace réservé. En règle générale, cette valeur doit correspondre au ratio de fractionnement de la règle de paire de fractionnement à laquelle l'espace réservé est associé.
  • splitMinWidthDp : spécifie la largeur minimale (840) requise pour que l'espace réservé s'affiche à l'écran avec l'activité principale. En règle générale, cette valeur doit correspondre à la largeur minimale de la règle de paire de fractionnement à laquelle l'espace réservé est associé. Les unités sont des pixels indépendants de l'affichage (dp).
  • finishPrimaryWithPlaceholder : indique si les activités du conteneur fractionné principal se terminent (toujours) lorsque l'espace réservé prend fin.
  • stickyPlaceholder : indique si l'espace réservé doit rester à l'écran (false) comme activité supérieure lorsque l'écran est redimensionné pour afficher deux volets, par exemple lorsqu'un appareil pliable est plié.
  • Inclut un filtre d'activité qui spécifie l'activité (ListActivity) avec laquelle l'espace réservé partage un fractionnement de la fenêtre de tâches.

L'espace réservé remplace l'activité secondaire de la règle de paire de fractionnement dont l'activité principale est identique à celle du filtre d'activité de l'espace réservé (voir "Règle de paire de fractionnement" dans la section "Configuration XML" de cet atelier de programmation).

Règle d'activité

Les règles d'activité sont des règles générales. Vous pouvez les spécifier pour les activités dont vous souhaitez qu'elles occupent l'intégralité de la fenêtre de tâches, à savoir pour les activités qui ne font jamais l'objet d'un fractionnement. Pour en savoir plus, consultez l'article Fenêtre modale plein écran du guide du développeur sur l'intégration d'activités.

Nous allons maintenant faire en sorte que l'activité récapitulative occupe l'intégralité de la fenêtre de tâches en la superposant à l'écran fractionné. Pour revenir au fractionnement, il suffira d'utiliser la navigation arrière.

Ajoutez la règle d'activité suivante au fichier de configuration :

main_split_config.xml

<!-- Activities that should never be in a split. -->
<ActivityRule
    window:alwaysExpand="true">
  <ActivityFilter
      window:activityName=".SummaryActivity"/>
</ActivityRule>

Cette règle a l'effet suivant :

  • Identifie l'activité qui doit s'afficher en plein écran (SummaryActivity).).
  • Configure les options de l'activité :
  • alwaysExpand : indique si l'activité doit se développer pour occuper tout l'espace d'affichage disponible.

Fichier source

Une fois terminé, votre fichier de configuration XML devrait se présenter comme suit :

main_split_config.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:window="http://schemas.android.com/apk/res-auto">

    <!-- Define a split for the named activity pair. -->
    <SplitPairRule
        window:splitRatio="0.33"
        window:splitMinWidthDp="840"
        window:finishPrimaryWithSecondary="never"
        window:finishSecondaryWithPrimary="always">
      <SplitPairFilter
          window:primaryActivityName=".ListActivity"
          window:secondaryActivityName=".DetailActivity"/>
    </SplitPairRule>

    <!-- Automatically launch a placeholder for the detail activity. -->
    <SplitPlaceholderRule
        window:placeholderActivityName=".PlaceholderActivity"
        window:splitRatio="0.33"
        window:splitMinWidthDp="840"
        window:finishPrimaryWithPlaceholder="always"
        window:stickyPlaceholder="false">
      <ActivityFilter
          window:activityName=".ListActivity"/>
    </SplitPlaceholderRule>

    <!-- Activities that should never be in a split. -->
    <ActivityRule
        window:alwaysExpand="true">
      <ActivityFilter
          window:activityName=".SummaryActivity"/>
    </ActivityRule>

</resources>

Créer une activité d'espace réservé

Vous devez créer une activité qui servira d'espace réservé spécifié dans le fichier de configuration XML. Cette activité peut être très simple et indiquer, par exemple, aux utilisateurs que le contenu finira par s'afficher ici.

Créez l'activité dans le dossier source principal de l'application exemple.

Dans Android Studio, procédez comme suit :

  1. Effectuez un clic droit sur le dossier source de l'application exemple, com.example.activity_embedding.
  2. Sélectionnez New > Activity > Empty Views Activity (Nouveau > Activité > Vues vides).
  3. Nommez l'activité PlaceholderActivity.
  4. Sélectionnez Finish (Terminer).

Android Studio crée alors l'activité dans le package de l'application exemple, l'ajoute au fichier manifeste de l'application et génère un fichier de ressources de mise en page nommé activity_placeholder.xml dans le dossier res/layout.

  1. Dans le fichier AndroidManifest.xml de l'application exemple, définissez le libellé de l'activité d'espace réservé sur une chaîne vide :

AndroidManifest.xml

<activity
    android:name=".PlaceholderActivity"
    android:exported="false"
    android:label="" />
  1. Remplacez le contenu du fichier de mise en page activity_placeholder.xml dans le dossier res/layout par le code suivant :

activity_placeholder.xml

<?xml version="1.0" encoding="utf-8"?>
<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:background="@color/gray"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".PlaceholderActivity">

  <TextView
      android:id="@+id/textViewPlaceholder"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/placeholder_text"
      android:textSize="36sp"
      android:textColor="@color/obsidian"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
  1. Enfin, ajoutez la ressource de chaîne suivante au fichier de ressources strings.xml dans le dossier res/values :

strings.xml

<string name="placeholder_text">Placeholder</string>

Créer un initialiseur

Le composant RuleController de WindowManager analyse les règles définies dans le fichier de configuration XML, puis les met à disposition du système.

Un initialiseur de la bibliothèque Jetpack Startup permet à RuleController d'accéder au fichier de configuration.

La bibliothèque Startup effectue l'initialisation des composants au démarrage de l'application. L'initialisation doit être effectuée avant le début des activités pour que RuleController ait accès aux règles de fractionnement et puisse les appliquer si nécessaire.

Ajouter la dépendance de la bibliothèque Startup

Pour activer la fonctionnalité de démarrage, ajoutez la dépendance de la bibliothèque Startup au fichier build.gradle de l'application exemple au niveau du module, par exemple :

build.gradle

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

Implémenter un initialiseur pour RuleController

Créez une implémentation de l'interface Startup Initializer.

Dans Android Studio, procédez comme suit :

  1. Effectuez un clic droit sur le dossier source de l'application exemple, com.example.activity_embedding.
  2. Sélectionnez New > Kotlin Class/File (Nouveau > Classe/Fichier Kotlin) ou New > Java Class (Nouveau > Classe Java).
  3. Nommez la classe SplitInitializer.
  4. Appuyez sur Entrée : Android Studio crée la classe dans le package de l'application exemple.
  5. Remplacez le contenu du fichier de classe par le suivant :

SplitInitializer.kt

package com.example.activity_embedding

import android.content.Context
import androidx.startup.Initializer
import androidx.window.embedding.RuleController

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

SplitInitializer.java

package com.example.activity_embedding;

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.startup.Initializer;
import androidx.window.embedding.RuleController;
import java.util.Collections;
import java.util.List;

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();
   }
}

L'initialiseur met les règles de fractionnement à disposition du composant RuleController en transmettant l'ID du fichier de ressources XML contenant les définitions (main_split_config) à la méthode parseRules() du composant. La méthode setRules() ajoute les règles analysées à RuleController.

Créer un fournisseur d'initialisation

Un fournisseur appelle le processus d'initialisation des règles de fractionnement.

Ajoutez androidx.startup.InitializationProvider à l'élément <application> du fichier manifeste de l'application exemple en tant que fournisseur, puis référencez 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 initialise SplitInitializer, qui appelle à son tour les méthodes RuleController qui analysent le fichier de configuration XML (main_split_config.xml) et ajoutent les règles à RuleController (voir "Implémenter un initialiseur pour RuleController" ci-dessus).

InitializationProvider détecte et initialise SplitInitializer avant l'exécution de la méthode onCreate() de l'application. Les règles de fractionnement sont donc appliquées lorsque l'activité principale de l'application commence.

Fichier source

Voici le fichier manifeste final de l'application :

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

  <application
      android:allowBackup="true"
      android:dataExtractionRules="@xml/data_extraction_rules"
      android:fullBackupContent="@xml/backup_rules"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:supportsRtl="true"
      android:theme="@style/Theme.Activity_Embedding"
      tools:targetApi="32">
    <activity
        android:name=".ListActivity"
        android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity
        android:name=".DetailActivity"
        android:exported="false"
        android:label="" />
    <activity
        android:name=".SummaryActivity"
        android:exported="false"
        android:label="" />
    <activity
        android:name=".PlaceholderActivity"
        android:exported="false"
        android:label="" />
    <property
        android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
        android:value="true" />
    <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>
  </application>

</manifest>

Raccourci d'initialisation

Si vous savez comment combiner des configurations XML avec les API WindowManager, vous pouvez supprimer l'initialiseur de la bibliothèque Startup et le fournisseur du fichier manifeste afin de simplifier l'implémentation.

Une fois le fichier de configuration XML créé, procédez comme suit :

Étape 1 : Créer une sous-classe de Application

Votre sous-classe d'application sera la première classe instanciée lors de la création du processus de votre application. Ajoutez les règles de fractionnement à RuleController dans la méthode onCreate() de votre sous-classe pour vous assurer qu'elles entrent en vigueur avant le lancement des activités.

Dans Android Studio, procédez comme suit :

  1. Effectuez un clic droit sur le dossier source de l'application exemple, com.example.activity_embedding.
  2. Sélectionnez New > Kotlin Class/File (Nouveau > Classe/Fichier Kotlin) ou New > Java Class (Nouveau > Classe Java).
  3. Nommez cette classe SampleApplication.
  4. Appuyez sur Entrée : Android Studio crée la classe dans le package de l'application exemple.
  5. Étendez la classe à partir du supertype Application.

SampleApplication.kt

package com.example.activity_embedding

import android.app.Application

/**
 * Initializer for activity embedding split rules.
 */
class SampleApplication : Application() {

}

SampleApplication.java

package com.example.activity_embedding;

import android.app.Application;

/**
 * Initializer for activity embedding split rules.
 */
public class SampleApplication extends Application {

}

Étape 2 : Initialiser RuleController

Ajoutez les règles de fractionnement du fichier de configuration XML à RuleController dans la méthode onCreate() de la sous-classe d'application.

Pour ajouter les règles à RuleController, procédez comme suit :

  1. Obtenez une instance singleton de RuleController.
  2. Utilisez la méthode RuleController associée Java statique ou Kotlin de parseRules() pour analyser le fichier XML.
  3. Ajoutez les règles analysées à RuleController avec la méthode setRules().

SampleApplication.kt

override fun onCreate() {
  super.onCreate()
  RuleController.getInstance(this)
    .setRules(RuleController.parseRules(this, R.xml.main_split_config))
}

SampleApplication.java

@Override
public void onCreate() {
  super.onCreate();
  RuleController.getInstance(this)
    .setRules(RuleController.parseRules(this, R.xml.main_split_config));
}

Étape 3 : Ajouter le nom de votre sous-classe au fichier manifeste

Ajoutez le nom de votre sous-classe à l'élément <application> du fichier manifeste de l'application :

AndroidManifest.xml

<application
    android:name=".SampleApplication"
    . . .

Exécuter le code

Créer et exécuter l'application exemple

Sur un téléphone non pliable, les activités sont toujours empilées, même en mode paysage :

Activité "Détail" (secondaire) superposée à l'activité "Liste" (principale ) en mode portrait sur un téléphone Activité "Détail" (secondaire) superposée à l'activité "Liste" (principale ) en mode paysage sur un téléphone

Sur Android 13 (niveau d'API 33) ou version antérieure, l'intégration d'activités n'est pas activée sur les téléphones non pliables, quelle que soit la largeur minimale de fractionnement.

La prise en charge de l'intégration d'activités pour les téléphones non pliables à des niveaux d'API plus élevés varie selon que le fabricant de l'appareil l'a activée.

Sur une petite tablette ou dans l'émulateur WSVGA 7 (tablette), les deux activités sont empilées en mode portrait, mais apparaissent côte à côte en mode paysage :

Activités "Liste" et "Détail" empilées en mode portrait sur une petite tablette Activités "Liste" et "Détail" côte à côte en mode paysage sur une petite tablette

Sur une grande tablette ou dans l'émulateur Pixel C, les activités sont empilées en mode portrait (voir "Format" ci-dessous), mais s'affichent côte à côte en mode paysage :

Activités "Liste" et "Détail" empilées en mode portrait sur une grande tablette Activités "Liste" et "Détail" côte à côte en mode paysage sur une grande tablette

Le résumé s'affiche en plein écran en mode paysage même s'il a été lancé depuis un écran fractionné :

Activité "Résumé" superposée au mode paysage sur une grande tablette

Format

Les fractionnements d'activité sont contrôlés par le format d'affichage en plus de la largeur minimale de fractionnement. Les attributs splitMaxAspectRatioInPortrait et splitMaxAspectRatioInLandscape spécifient le format maximal (height:width) pour lequel les fractionnements d'activité sont affichés. Les attributs représentent les propriétés maxAspectRatioInPortrait et maxAspectRatioInLandscape de SplitRule.

Si le format d'un écran dépasse cette valeur dans les deux sens, les écrans fractionnés sont désactivés, quelle que soit la largeur de l'écran. La valeur par défaut pour l'orientation portrait est de 1,4 (voir SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT), ce qui empêche les longs écrans étroits d'inclure les écrans fractionnés. Par défaut, les fractionnements sont toujours autorisés en mode paysage (voir SPLIT_MAX_ASPECT_RATIO_LANDSCAPE_DEFAULT).

La largeur d'affichage en mode portrait de l'émulateur Pixel C est de 900 dp, ce qui est plus large que le paramètre splitMinWidthDp dans le fichier de configuration XML de l'application exemple. L'émulateur devrait donc afficher un fractionnement d'activité. Toutefois, le format de la Pixel C en mode portrait est supérieur à 1,4, ce qui empêche les fractionnements d'activités de s'afficher en mode portrait.

Vous pouvez définir le format maximal pour les affichages portrait et paysage dans les éléments SplitPairRule et SplitPlaceholderRule du fichier de configuration XML. Par exemple :

main_split_config.xml

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:window="http://schemas.android.com/apk/res/android">

  <!-- Define a split for the named activity pair. -->
  <SplitPairRule
      . . .
      window:splitMaxAspectRatioInPortrait="alwaysAllow"
      window:splitMaxAspectRatioInLandscape="alwaysDisallow"
      . . .
 </SplitPairRule>

  <SplitPlaceholderRule
      . . .
      window:splitMaxAspectRatioInPortrait="alwaysAllow"
      window:splitMaxAspectRatioInLandscape="alwaysDisallow"
      . . .
  </SplitPlaceholderRule>

</resources>

Sur une grande tablette dont la largeur d'écran en mode portrait est supérieure ou égale à 840 dp, ou avec l'émulateur Pixel C, les activités sont côte à côte en mode portrait, mais empilées en mode paysage :

Activités "Liste" et "Détail" côte à côte en mode portrait sur une grande tablette Activités "Liste" et "Détail" empilées en mode paysage sur une grande tablette

Bonus

Essayez de définir le format dans l'application exemple, comme indiqué ci-dessus pour les orientations portrait et paysage. Testez les paramètres avec votre grande tablette (si la largeur du portrait est d'au moins 840 dp) ou avec l'émulateur Pixel C. Vous devriez voir un fractionnement d'activité en mode portrait, mais pas en mode paysage.

Déterminez le format portrait de votre grande tablette (celui de la Pixel C est légèrement supérieur à 1,4). Définissez des valeurs supérieures et inférieures au format pour splitMaxAspectRatioInPortrait. Exécutez l'application et observez les résultats.

6. API WindowManager

Vous pouvez activer l'intégration d'activités dans le code avec une seule méthode appelée depuis la méthode onCreate() de l'activité qui lance le fractionnement. Si vous préférez utiliser du code plutôt que XML, il s'agit de la marche à suivre.

Ajouter la dépendance WindowManager

Que vous créiez une implémentation XML ou que vous utilisiez des appels d'API, votre application doit avoir accès à la bibliothèque WindowManager. Consultez la section "Configuration XML" de cet atelier de programmation pour découvrir comment ajouter la dépendance WindowManager à votre application.

Informer le système

Que vous utilisiez un fichier de configuration XML ou des appels d'API WindowManager, votre application doit avertir le système qu'elle a implémenté l'intégration d'activités. Consultez la section "Configuration XML" de cet atelier de programmation pour découvrir comment informer le système de votre implémentation.

Créer une classe pour gérer les fractionnements

Dans cette section de l'atelier de programmation, vous allez entièrement implémenter un fractionnement d'activité dans une seule méthode d'objet statique ou associée que vous appellerez à partir de l'activité principale de l'application exemple, ListActivity.

Créez une classe nommée SplitManager avec une méthode nommée createSplit qui inclut un paramètre context (certains appels d'API nécessitent ce paramètre) :

SplitManager.kt

class SplitManager {

    companion object {

        fun createSplit(context: Context) {
        }
}

SplitManager.java

class SplitManager {

    static void createSplit(Context context) {
    }
}

Appelez cette méthode dans la méthode onCreate() d'une sous-classe de la classe Application.

Pour découvrir pourquoi et comment sous-classer Application, consultez "Raccourci d'initialisation" dans la section "Configuration XML" de cet atelier de programmation.

SampleApplication.kt

package com.example.activity_embedding

import android.app.Application

/**
 * Initializer for activity embedding split rules.
 */
class SampleApplication : Application() {

  override fun onCreate() {
    super.onCreate()
    SplitManager.createSplit(this)
  }
}

SampleApplication.java

package com.example.activity_embedding;

import android.app.Application;

/**
 * Initializer for activity embedding split rules.
 */
public class SampleApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    SplitManager.createSplit(this);
  }
}

Créer une règle de fractionnement

API requises :

SplitPairRule définit une règle de fractionnement pour une paire d'activités.

SplitPairRule.Builder crée une SplitPairRule. Le compilateur utilise un ensemble d'objets SplitPairFilter comme argument. Les filtres spécifient quand appliquer la règle.

Vous devez enregistrer la règle avec une instance singleton du composant RuleController, ce qui met les règles de fractionnement à disposition du système.

Pour créer une règle de fractionnement, procédez comme suit :

  1. Créez un filtre de paire de fractionnement qui identifie ListActivity et DetailActivity comme les activités qui partagent un fractionnement :

SplitManager.kt/createSplit()

val splitPairFilter = SplitPairFilter(
    ComponentName(context, ListActivity::class.java),
    ComponentName(context, DetailActivity::class.java),
    null
)

SplitManager.java/createSplit()

SplitPairFilter splitPairFilter = new SplitPairFilter(
    new ComponentName(context, ListActivity.class),
    new ComponentName(context, DetailActivity.class),
    null
);

Le filtre peut inclure une action d'intent (troisième paramètre) pour le lancement de l'activité secondaire. Si vous incluez une action d'intent, le filtre la recherche avec le nom de l'activité. Pour les activités de votre propre application, vous ne filtrerez probablement pas les actions d'intent. L'argument peut donc être nul.

  1. Ajoutez le filtre à un ensemble de filtres :

SplitManager.kt/createSplit()

val filterSet = setOf(splitPairFilter)

SplitManager.java/createSplit()

Set<SplitPairFilter> filterSet = new HashSet<>();
filterSet.add(splitPairFilter);
  1. Créez des attributs de mise en page pour le fractionnement :

SplitManager.kt/createSplit()

val splitAttributes: SplitAttributes = SplitAttributes.Builder()
      .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
      .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
      .build()

SplitManager.java/createSplit()

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 l'écran occupé par le 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).
  1. Créez une règle de paire de fractionnement :

SplitManager.kt/createSplit()

val splitPairRule = SplitPairRule.Builder(filterSet)
      .setDefaultSplitAttributes(splitAttributes)
      .setMinWidthDp(840)
      .setMinSmallestWidthDp(600)
      .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
      .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
      .setClearTop(false)
      .build()

SplitManager.java/createSplit()

SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
  .setDefaultSplitAttributes(splitAttributes)
  .setMinWidthDp(840)
  .setMinSmallestWidthDp(600)
  .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. Dans l'application exemple, ListActivity et DetailActivity sont spécifiés dans un filtre de paire de fractionnement (voir les étapes précédentes).
  • 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.
  • 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 ont pris fin (voir 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 dans le conteneur secondaire lorsque toutes les activités du conteneur principal ont pris fin (voir Arrêter les activités).
  • setClearTop : précise si toutes les activités dans le conteneur secondaire sont arrêtées lorsqu'une nouvelle activité est lancée dans le conteneur. La valeur "false" indique que les nouvelles activités sont empilées sur les activités déjà présentes dans le conteneur secondaire.
  1. Obtenez l'instance du singleton du RuleController de WindowManager, puis ajoutez la règle :

SplitManager.kt/createSplit()

val ruleController = RuleController.getInstance(context)
ruleController.addRule(splitPairRule)

SplitManager.java/createSplit()

RuleController ruleController = RuleController.getInstance(context);
ruleController.addRule(splitPairRule);

Créer une règle d'espace réservé

API requises :

SplitPlaceholderRule définit une règle pour une activité qui occupe le conteneur secondaire lorsqu'aucun contenu n'est disponible pour ce conteneur spécifique. Pour créer une activité d'espace réservé, consultez "Créer une activité d'espace réservé" dans la section "Configuration XML" de cet atelier de programmation. Pour en savoir plus, consultez la section Espaces réservés dans le guide du développeur sur l'intégration d'activités.

SplitPlaceholderRule.Builder crée une SplitPlaceholderRule. Le compilateur utilise un ensemble d'objets ActivityFilter comme argument. Les objets spécifient les activités auxquelles la règle d'espace réservé est associée. Si le filtre correspond à une activité démarrée, le système applique la règle d'espace réservé.

Vous devez enregistrer la règle avec le composant RuleController.

Pour créer une règle d'espace réservé pour un fractionnement, procédez comme suit :

  1. Créez un ActivityFilter :

SplitManager.kt/createSplit()

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

SplitManager.java/createSplit()

ActivityFilter placeholderActivityFilter = new ActivityFilter(
    new ComponentName(context, ListActivity.class),
    null
);

Le filtre associe la règle à l'activité principale de l'application exemple, ListActivity. Ainsi, lorsqu'aucun contenu détaillé n'est disponible dans la mise en page Liste/Détail, l'espace réservé remplit la zone de détail.

Le filtre peut inclure une action d'intent (deuxième paramètre) pour le lancement de l'activité associée (lancement de ListActivity). Si vous incluez une action d'intent, le filtre la recherche avec le nom de l'activité. Pour les activités de votre propre application, vous ne filtrerez probablement pas les actions d'intent. L'argument peut donc être nul.

  1. Ajoutez le filtre à un ensemble de filtres :

SplitManager.kt/createSplit()

val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

SplitManager.java/createSplit()

Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
placeholderActivityFilterSet.add(placeholderActivityFilter);
  1. Créez une SplitPlaceholderRule :

SplitManager.kt/createSplit()

val splitPlaceholderRule = SplitPlaceholderRule.Builder(
      placeholderActivityFilterSet,
      Intent(context, PlaceholderActivity::class.java)
    ).setDefaultSplitAttributes(splitAttributes)
     .setMinWidthDp(840)
     .setMinSmallestWidthDp(600)
     .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
     .build()

SplitManager.java/createSplit()

SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
  placeholderActivityFilterSet,
  new Intent(context, PlaceholderActivity.class)
).setDefaultSplitAttributes(splitAttributes)
 .setMinWidthDp(840)
 .setMinSmallestWidthDp(600)
 .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
 .build();

SplitPlaceholderRule.Builder crée et configure la règle :

  • placeholderActivityFilterSet : contient les 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.
  • 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 indique que le système doit toujours terminer les activités dans le conteneur principal lorsque l'espace réservé a pris fin (voir Arrêter les activités).
  1. Ajoutez la règle à la fenêtre RuleController de WindowManager :

SplitManager.kt/createSplit()

ruleController.addRule(splitPlaceholderRule)

SplitManager.java/createSplit()

ruleController.addRule(splitPlaceholderRule);

Créer une règle d'activité

API requises :

ActivityRule permet de définir une règle pour une activité qui occupera la totalité de la fenêtre de tâches, comme une boîte de dialogue modale. Pour en savoir plus, consultez l'article Fenêtre modale plein écran du guide du développeur sur l'intégration d'activités.

SplitPlaceholderRule.Builder crée une SplitPlaceholderRule. Le compilateur utilise un ensemble d'objets ActivityFilter comme argument. Les objets spécifient les activités auxquelles la règle d'espace réservé est associée. Si le filtre correspond à une activité démarrée, le système applique la règle d'espace réservé.

Vous devez enregistrer la règle avec le composant RuleController.

Pour créer une règle d'activité, procédez comme suit :

  1. Créez un ActivityFilter :

SplitManager.kt/createSplit()

val summaryActivityFilter = ActivityFilter(
    ComponentName(context, SummaryActivity::class.java),
    null
)

SplitManager.java/createSplit()

ActivityFilter summaryActivityFilter = new ActivityFilter(
    new ComponentName(context, SummaryActivity.class),
    null
);

Le filtre spécifie l'activité à laquelle la règle s'applique, SummaryActivity.

Le filtre peut inclure une action d'intent (deuxième paramètre) pour le lancement de l'activité associée (lancement de SummaryActivity). Si vous incluez une action d'intent, le filtre la recherche avec le nom de l'activité. Pour les activités de votre propre application, vous ne filtrerez probablement pas les actions d'intent. L'argument peut donc être nul.

  1. Ajoutez le filtre à un ensemble de filtres :

SplitManager.kt/createSplit()

val summaryActivityFilterSet = setOf(summaryActivityFilter)

SplitManager.java/createSplit()

Set<ActivityFilter> summaryActivityFilterSet = new HashSet<>();
summaryActivityFilterSet.add(summaryActivityFilter);
  1. Créez une ActivityRule :

SplitManager.kt/createSplit()

val activityRule = ActivityRule.Builder(summaryActivityFilterSet)
      .setAlwaysExpand(true)
      .build()

SplitManager.java/createSplit()

ActivityRule activityRule = new ActivityRule.Builder(
    summaryActivityFilterSet
).setAlwaysExpand(true)
 .build();

ActivityRule.Builder crée et configure la règle :

  • summaryActivityFilterSet : 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 se développer pour occuper tout l'espace d'affichage disponible.
  1. Ajoutez la règle à la fenêtre RuleController de WindowManager :

SplitManager.kt/createSplit()

ruleController.addRule(activityRule)

SplitManager.java/createSplit()

ruleController.addRule(activityRule);

Exécuter le code

Créer et exécuter l'application exemple

L'application devrait se comporter de la même manière que lorsqu'elle est personnalisée à l'aide d'un fichier de configuration XML.

Consultez la section "Exécuter le code" dans la section "Configuration XML" de cet atelier de programmation.

Bonus

Essayez de définir le format dans l'application exemple à l'aide des méthodes setMaxAspectRatioInPortrait et setMaxAspectRatioInLandscape de SplitPairRule.Builder et SplitPlaceholderRule.Builder. Spécifiez les valeurs avec les propriétés et les méthodes de la classe EmbeddingAspectRatio, par exemple :

SplitPairRule.Builder(filterSet)
  . . .
  .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
  . . .
.build()

Testez les paramètres avec votre grande tablette ou l'émulateur Pixel C.

Déterminez le format portrait de votre grande tablette (celui de la Pixel C est légèrement supérieur à 1,4). Définissez des valeurs supérieures et inférieures au format de votre tablette ou de la Pixel C pour le format maximal en mode portrait. Essayez les propriétés ALWAYS_ALLOW et ALWAYS_DISALLOW.

Exécutez l'application et observez les résultats.

Pour en savoir plus, consultez la section "Format" dans la section "Configuration XML" de cet atelier de programmation.

7. Navigation Material Design

Les consignes Material Design prévoient différents composants de navigation en fonction de la taille de l'écran : un rail de navigation pour les écrans d'une taille supérieure ou égale à 840 dp, une barre de navigation inférieure pour les écrans d'une taille inférieure à 840 dp.

fb47462060f4818d.gif

Avec l'intégration d'activités, vous ne pouvez pas utiliser les méthodes getCurrentWindowMetrics() et getMaximumWindowMetrics() de WindowManager pour déterminer la largeur de l'écran, car les métriques de fenêtre renvoyées par ces méthodes décrivent le volet d'affichage qui contient l'activité intégrée qui a appelé ces méthodes.

Pour obtenir les dimensions exactes de votre application d'intégration d'activités, utilisez un calculateur d'attributs de fractionnement et SplitAttributesCalculatorParams.

Supprimez les lignes suivantes si vous les avez ajoutées dans une section précédente.

main_split_config.xml

<SplitPairRule
    . . .
    window:splitMaxAspectRatioInPortrait="alwaysAllow" // Delete this line.
    window:splitMaxAspectRatioInLandscape="alwaysDisallow" // Delete this line.
    . . .>
</SplitPairRule>

<SplitPlaceholderRule
    . . .

    window:splitMaxAspectRatioInPortrait="alwaysAllow" // Delete this line.
    window:splitMaxAspectRatioInLandscape="alwaysDisallow" // Delete this line.
    . . .>
<SplitPlaceholderRule/>

Navigation flexible

Pour changer dynamiquement les composants de navigation en fonction de la taille de l'écran, utilisez un calculateur SplitAttributes. Le calculateur détecte les changements d'orientation de l'appareil ainsi que la taille de la fenêtre, et recalcule les dimensions de l'écran en conséquence. Nous intégrerons un calculateur à SplitController afin de déclencher les modifications des composants de navigation en fonction des changements de taille de l'écran.

Créer une mise en page de navigation

Tout d'abord, créez un menu que nous utiliserons pour remplir le rail et la barre de navigation.

Dans le dossier res/menu, créez un nouveau fichier de ressources de menu que vous nommerez nav_menu.xml. Remplacez le contenu du fichier de menu par le suivant :

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/navigation_home"
        android:title="Home" />
    <item
        android:id="@+id/navigation_dashboard"
        android:title="Dashboard" />
    <item
        android:id="@+id/navigation_settings"
        android:title="Settings" />
</menu>

Ensuite, ajoutez une barre de navigation et un rail de navigation à votre mise en page. Définissez leur visibilité sur gone pour qu'ils soient initialement masqués. Nous les rendrons visibles en fonction des dimensions de la mise en page ultérieurement.

activity_list.xml

<com.google.android.material.navigationrail.NavigationRailView
     android:id="@+id/navigationRailView"
     android:layout_width="wrap_content"
     android:layout_height="match_parent"
     app:layout_constraintStart_toStartOf="parent"
     app:layout_constraintTop_toTopOf="parent"
     app:menu="@menu/nav_menu"
     android:visibility="gone" />

<com.google.android.material.bottomnavigation.BottomNavigationView
   android:id="@+id/bottomNavigationView"
   android:layout_width="0dp"
   android:layout_height="wrap_content"
   app:menu="@menu/nav_menu"
   app:layout_constraintBottom_toBottomOf="parent"
   app:layout_constraintStart_toStartOf="parent"
   app:layout_constraintEnd_toEndOf="parent"
   android:visibility="gone" />

Écrivez une fonction pour gérer le basculement entre la barre de navigation et le rail de navigation.

ListActivity.kt / setWiderScreenNavigation()

private fun setWiderScreenNavigation(useNavRail: Boolean) {
   val navRail = findViewById(R.id.navigationRailView)
   val bottomNav = findViewById(R.id.bottomNavigationView)
   if (useNavRail) {
       navRail.visibility = View.VISIBLE
       bottomNav.visibility = View.GONE
   } else {
       navRail.visibility = View.GONE
       bottomNav.visibility = View.VISIBLE
   }
}

ListActivity.java / setWiderScreenNavigation()

private void setWiderScreenNavigation(boolean useNavRail) {
   NavigationRailView navRail = findViewById(R.id.navigationRailView);
   BottomNavigationView bottomNav = findViewById(R.id.bottomNavigationView);
   if (useNavRail) {
       navRail.setVisibility(View.VISIBLE);
       bottomNav.setVisibility(View.GONE);
   } else {
       navRail.setVisibility(View.GONE);
       bottomNav.setVisibility(View.VISIBLE);
   }
}

Calculateur d'attributs de fractionnement

SplitController obtient des informations sur les fractionnements d'activités actuellement actives et fournit des points d'interaction permettant de personnaliser les fractionnements et d'en créer de nouvelles.

Dans les sections précédentes, nous avons défini les attributs par défaut des fractionnements en spécifiant splitRatio et d'autres attributs dans les balises <SplitPairRule> et <SplitPlaceHolderRule> des fichiers XML ou en utilisant les API SplitPairRule.Builder#setDefaultSplitAttributes() et SplitPlaceholderRule.Builder#setDefaultSplitAttributes().

Les attributs de fractionnement par défaut sont appliqués si les WindowMetrics du conteneur parent satisfont aux exigences de dimension de SplitRule, à savoir minWidthDp, minHeightDp et minSmallestWidthDp.

Nous allons mettre en place un calculateur d'attributs de fractionnement pour remplacer les attributs de fractionnement par défaut. Le calculateur met à jour les paires de fractionnement existantes après un changement d'état de la fenêtre ou de l'appareil, tel qu'un changement d'orientation ou un changement d'état de pliage.

Cela permet aux développeurs d'apprendre l'état de l'appareil ou de la fenêtre et de définir différents attributs de fractionnement dans différents scénarios, y compris l'orientation portrait et paysage ainsi que la position écran à plat.

Lors de la création d'un calculateur d'attributs de fractionnement, la plate-forme transmet un objet SplitAttributesCalculatorParams à la fonction setSplitAttributesCalculator(). La propriété parentWindowMetrics fournit des métriques sur les fenêtres d'application.

Dans le code suivant, l'activité vérifie si les contraintes par défaut sont respectées, c'est-à-dire largeur > 840 dp et plus petite largeur > 600 dp. Lorsque les conditions sont remplies, les activités sont intégrées dans une mise en page à double volet et l'application utilise un rail de navigation au lieu d'une barre de navigation inférieure. Dans le cas contraire, les activités sont affichées en plein écran avec une barre de navigation inférieure.

ListActivity.kt / setSplitAttributesCalculator()

SplitController.getInstance(this).setSplitAttributesCalculator
       params ->

   if (params.areDefaultConstraintsSatisfied) {
       // When default constraints are satisfied, use the navigation rail.
       setWiderScreenNavigation(true)
       return@setSplitAttributesCalculator params.defaultSplitAttributes
   } else {
       // Use the bottom navigation bar in other cases.
       setWiderScreenNavigation(false)
       // Expand containers if the device is in portrait or the width is less than 840 dp.
       SplitAttributes.Builder()
           .setSplitType(SPLIT_TYPE_EXPAND)
           .build()
   }
}

ListActivity.java / setSplitAttributesCalculator()

SplitController.getInstance(this).setSplitAttributesCalculator(params -> {
   if (params.areDefaultConstraintsSatisfied()) {
       // When default constraints are satisfied, use the navigation rail.
       setWiderScreenNavigation(true);
       return params.getDefaultSplitAttributes();
   } else {
       // Use the bottom navigation bar in other cases.
       setWiderScreenNavigation(false);
       // Expand containers if the device is in portrait or the width is less than 600 dp.
       return new SplitAttributes.Builder()
               .setSplitType(SplitType.SPLIT_TYPE_EXPAND)
               .build();
   }
});

Bravo, votre application d'intégration d'activités respecte désormais les consignes de navigation Material Design !

8. Félicitations !

Bravo ! Vous venez d'optimiser une application basée sur des activités pour une mise en page Liste/Détail sur les grands écrans et d'ajouter la navigation Material Design.

Vous avez appris deux façons d'implémenter l'intégration d'activités :

  • Via un fichier de configuration XML
  • Via des appels d'API Jetpack
  • Via l'implémentation d'une navigation flexible avec l'intégration d'activités

Vous n'avez pas eu à réécrire le code source Kotlin ou Java de l'application.

Vous êtes maintenant prêt à optimiser vos applications de production pour les grands écrans grâce à l'intégration d'activités.

9. En savoir plus