Gestionnaire de fenêtres Jetpack pour les appareils pliables

Le Gestionnaire de fenêtres Jetpack fournit une API standard à utiliser avec tous les appareils pliables. Il contient deux classes importantes :

  • DisplayFeature : identifie les perturbations de la surface continue de l’écran plat, comme des charnières ou des plis. Le gestionnaire de fenêtres retourne une collection de fonctionnalités d’affichage à partir d’un rappel de modification de disposition.
  • FoldingFeature : fournit des informations sur une fonctionnalité spécifique de l’appareil. Surface Duo n’a qu’une seule fonctionnalité de pliage, mais d’autres appareils peuvent en avoir plus.

Un guide similaire se trouve dans le Codelab pliable Android. Découvrez plus en détail le développement pour les appareils pliables dans la documentation Android. Des exemples de l’équipe Android sont également disponibles sur GitHub. Les notes de publication Jetpack enregistrent les modifications dans le Gestionnaire de fenêtres à mesure de la mise à jour.

Conseil

Les contrôles et les classes d’assistance de la bibliothèque double écran Surface Duo fonctionnent avec le Gestionnaire de fenêtres. Suivez les instructions permettant d’ajouter les packages appropriés à votre projet d’application.

Pour utiliser le Gestionnaire de fenêtres directement dans votre code, suivez les instructions ci-dessous :

Ajout de dépendances

  1. Vérifiez que vous disposez du référentiel mavenCentral() dans votre fichier build.gradle de premier niveau :

    allprojects {
        repositories {
            google()
            mavenCentral()
         }
    }
    
  2. Vérifiez que compileSdkVersion et targetSdkVersion sont définis sur API 31 ou version ultérieure dans le fichier build.gradle au niveau du module :

    android { 
        compileSdkVersion 31
    
        defaultConfig { 
            targetSdkVersion 31
        } 
        ... 
    }
    
    
  3. Ajoutez les dépendances suivantes à votre fichier build.gradle de niveau module :

    dependencies {
        implementation "androidx.window:window:1.0.0"
        implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
    }
    

Utiliser le Gestionnaire de fenêtres dans votre code Kotlin

Lors de l’accès aux propriétés du Gestionnaire de fenêtres dans vos projets Kotlin, il est important de configurer le flux d’informations approprié. Sinon, vous pouvez recevoir trop ou trop peu de mises à jour d’événements, et les performances des applications peuvent en être affectées.

Pour initialiser et utiliser un objet WindowInfoTracker, suivez les étapes ci-dessous :

  1. Dans votre classe MainActivity, créez une variable pour WindowInfoTracker. Vérifiez que import androidx.window.layout.WindowInfoTracker est ajouté en haut du fichier.

    class MainActivity : AppCompatActivity() {
        private lateinit var windowInfoTracker: WindowInfoTracker
    
  2. Initialisez WindowInfoTracker dans la méthode onCreate de votre activité et configurez un flux pour collecter des informations à partir de la propriété windowLayoutInfo.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        // Initialize the window manager
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        // Set up a flow
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { 
                        // Check FoldingFeature properties here
                    }
            }
        }
    }
    

    Vérifiez que ces importations sont également ajoutées en haut du fichier :

    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.lifecycleScope
    import androidx.lifecycle.repeatOnLifecycle
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.flow.collect
    import kotlinx.coroutines.launch
    
  3. Ajoutez du code pour vérifier le flux WindowLayoutInfo pour les propriétés des fonctionnalités de pliage. Quand ce code est exécuté, l’activité est mise à jour avec la position de l’appareil et les fonctionnalités d’affichage actuelles (si l’affichage est réparti de part et d’autre d’un pli ou d’une charnière).

    Dans l’extrait de code ci-dessous, l’activité affiche un texte différent en fonction des propriétés d’un FoldingFeature.

    Cet exemple contient un TextView appelé layout_change_text qui montre le type d’occlusion et la valeur isSeparating pour toutes les fonctionnalités de pliage détectées.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    
        windowInfoTracker = WindowInfoTracker.getOrCreate(this@MainActivity)
    
        lifecycleScope.launch(Dispatchers.Main) {
            lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
                windowInfoTracker.windowLayoutInfo(this@MainActivity)
                    .collect { newLayoutInfo ->
                        layout_change_text.text = "No display features detected"
                        for (displayFeature : DisplayFeature in newLayoutInfo.displayFeatures) {
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.NONE) {
                                layout_change_text.text = "App is spanned across a fold, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                            if (displayFeature is FoldingFeature && displayFeature.occlusionType == FoldingFeature.OcclusionType.FULL) {
                                layout_change_text.text = "App is spanned across a hinge, " +
                                    "isSeparating = ${displayFeature.isSeparating}"
                            }
                        }
                    }
            }
        }
    }
    

Propriétés des fonctionnalités de pliage

Dans la collection d’éléments DisplayFeature de la classe WindowLayoutInfo, un ou plusieurs de ces éléments peuvent être des instances de la classe FoldingFeature.

Les fonctionnalités de pliage ont les propriétés suivantes :

  • bounds - coordonnées du rectangle englobant d’une fonctionnalité de pliage
  • occlusionType - si une fonctionnalité de pliage masque le contenu (FULL ou NONE)
  • orientation - orientation d’une fonctionnalité de pliage (HORIZONTAL ou VERTICAL)
  • state - angle d’une fonctionnalité de pliage (HALF_OPENED ou FLAT)
  • isSeparating - si une fonctionnalité de pliage sépare la zone d’affichage en deux sections distinctes

Vous pouvez interroger ces propriétés pour prendre des décisions sur la façon d’ajuster votre disposition après les modifications de configuration.

isSeparating

Lorsque vous décidez de l’endroit où placer des contrôles ou du nombre de volets de contenu à afficher, utilisez la propriété isSeparating. Ce champ garantit que votre application offre la meilleure expérience utilisateur sur tous les appareils pliables :

  • Pour les appareils double écran, c’est toujours vrai lorsqu’une application est répartie de part et d’autre de la charnière
  • Pour les autres appareils pliables, c’est vrai seulement si l’état est HALF_OPENED, par exemple lorsqu’un appareil est en position table

Utilisez la propriété isSeparating pour décider s’il faut adapter la disposition de l’interface utilisateur de votre application pour un appareil pliable ou utiliser l’interface utilisateur par défaut lorsqu’il n’existe aucune séparation :

private fun updateCurrentLayout(newLayoutInfo: WindowLayoutInfo) {
   for (displayFeature in newLayoutInfo.displayFeatures) {
       val foldFeature = displayFeature as? FoldingFeature
       foldFeature?.let {
           if (it.isSeparating) {
               // The content is separated by the FoldingFeature.
               // Here is where you should adapt your UI.
           } else {
               // The content is not separated.
               // Users can see and interact with your UI properly.
           }
       }
   }
}

Pour voir un exemple plus élaboré de l’utilisation de ce champ, consultez cet exemple isSeparating.

Google fournit également de la documentation et des exemples sur cette propriété dans le cadre de ses conseils sur les appareils grand écran et pliables.

Exemples

Le dépôt GitHub surface-duo-jetpack-window-manager-samples contient un nombre d’exemples Kotlin illustrant différents modèles d’expérience utilisateur du double écran créés avec le Gestionnaire de fenêtres Jetpack et le système d’affichage traditionnel.

Le dépôt surface-duo-compose-samples GitHub dispose également d’exemples Kotlin double écran qui utilisent le Gestionnaire de fenêtres Jetpack, mais dans ces exemples, l’interface utilisateur est générée avec Jetpack Compose.

API Java

Reportez-vous au billet de blog sur le Gestionnaire de fenêtres Jetpack avec Java et à cet exemple Java pour voir comment accéder à la classe WindowInfoTracker via WindowInfoTrackerCallbackAdapter.

Ressources