Migrer CoordinatorLayout vers Compose

CoordinatorLayout est un ViewGroup qui permet des mises en page complexes, superposées et imbriquées. Il sert de conteneur pour permettre des interactions Material Design spécifiques, telles que les barres d'outils et les bottom sheets qui se développent/se rétractent, pour les vues qui s'y trouvent.

Dans Compose, l'équivalent le plus proche d'un CoordinatorLayout est un Scaffold. Un Scaffold fournit des emplacements de contenu pour combiner des composants Material à des modèles et des interactions d'écran courants. Cette page explique comment migrer votre implémentation CoordinatorLayout pour utiliser Scaffold dans Compose.

Procédure de migration

Pour migrer CoordinatorLayout vers Scaffold, procédez comme suit:

  1. Dans l'extrait de code ci-dessous, CoordinatorLayout contient un AppBarLayout contenant un ToolBar, un ViewPager et un FloatingActionButton. Commentez CoordinatorLayout et ses enfants dans la hiérarchie de l'UI, puis ajoutez ComposeView pour le remplacer.

    <!--  <androidx.coordinatorlayout.widget.CoordinatorLayout-->
    <!--      android:id="@+id/coordinator_layout"-->
    <!--      android:layout_width="match_parent"-->
    <!--      android:layout_height="match_parent"-->
    <!--      android:fitsSystemWindows="true">-->
    
    <!--    <androidx.compose.ui.platform.ComposeView-->
    <!--        android:id="@+id/compose_view"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="match_parent"-->
    <!--        app:layout_behavior="@string/appbar_scrolling_view_behavior" />-->
    
    <!--    <com.google.android.material.appbar.AppBarLayout-->
    <!--        android:id="@+id/app_bar_layout"-->
    <!--        android:layout_width="match_parent"-->
    <!--        android:layout_height="wrap_content"-->
    <!--        android:fitsSystemWindows="true"-->
    <!--        android:theme="@style/Theme.Sunflower.AppBarOverlay">-->
    
        <!-- AppBarLayout contents here -->
    
    <!--    </com.google.android.material.appbar.AppBarLayout>-->
    
    <!--  </androidx.coordinatorlayout.widget.CoordinatorLayout>-->
    
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    
  2. Dans votre fragment ou votre activité, obtenez une référence au ComposeView que vous venez d'ajouter et appelez la méthode setContent dessus. Dans le corps de la méthode, définissez un Scaffold comme contenu:

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            // Scaffold contents
            // ...
        }
    }

  3. Dans le contenu de votre Scaffold, ajoutez-y le contenu principal de votre écran. Étant donné que le contenu principal du fichier XML ci-dessus est un ViewPager2, nous utiliserons un HorizontalPager, qui en est l'équivalent Compose. L'expression lambda content de Scaffold reçoit également une instance de PaddingValues à appliquer à la racine du contenu. Vous pouvez utiliser Modifier.padding pour appliquer le même PaddingValues à HorizontalPager.

    composeView.setContent {
        Scaffold(Modifier.fillMaxSize()) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

  4. Utilisez les autres emplacements de contenu fournis par Scaffold pour ajouter d'autres éléments d'écran et migrer les vues enfants restantes. Vous pouvez utiliser l'emplacement topBar pour ajouter un TopAppBar et l'emplacement floatingActionButton pour fournir un FloatingActionButton.

    composeView.setContent {
        Scaffold(
            Modifier.fillMaxSize(),
            topBar = {
                TopAppBar(
                    title = {
                        Text("My App")
                    }
                )
            },
            floatingActionButton = {
                FloatingActionButton(
                    onClick = { /* Handle click */ }
                ) {
                    Icon(
                        Icons.Filled.Add,
                        contentDescription = "Add Button"
                    )
                }
            }
        ) { contentPadding ->
            val pagerState = rememberPagerState {
                10
            }
            HorizontalPager(
                state = pagerState,
                modifier = Modifier.padding(contentPadding)
            ) { /* Page contents */ }
        }
    }

Cas d'utilisation courants

Réduire et développer les barres d'outils

Dans le système de vue, pour réduire et développer la barre d'outils avec CoordinatorLayout, vous utilisez un AppBarLayout comme conteneur pour la barre d'outils. Vous pouvez ensuite spécifier un Behavior via layout_behavior en XML sur la vue déroulante associée (comme RecyclerView ou NestedScrollView) pour déclarer comment la barre d'outils se réduit/s'étend lorsque vous faites défiler l'écran.

Dans Compose, vous pouvez obtenir un effet similaire à l'aide d'un TopAppBarScrollBehavior. Par exemple, pour implémenter une barre d'outils réductible/dépliable afin qu'elle s'affiche lorsque vous faites défiler l'écran vers le haut, procédez comme suit:

  1. Appelez TopAppBarDefaults.enterAlwaysScrollBehavior() pour créer un TopAppBarScrollBehavior.
  2. Fournissez l'TopAppBarScrollBehavior créé au TopAppBar.
  3. Connectez NestedScrollConnection via Modifier.nestedScroll sur Scaffold afin que l'échafaudage puisse recevoir des événements de défilement imbriqués lorsque le contenu à faire défiler défile vers le haut/le bas. De cette manière, la barre d'application contenue peut se réduire/développer de manière appropriée à mesure que le contenu défile.

    // 1. Create the TopAppBarScrollBehavior
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("My App")
                },
                // 2. Provide scrollBehavior to TopAppBar
                scrollBehavior = scrollBehavior
            )
        },
        // 3. Connect the scrollBehavior.nestedScrollConnection to the Scaffold
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(scrollBehavior.nestedScrollConnection)
    ) { contentPadding ->
        /* Contents */
        // ...
    }

Personnaliser l'effet de défilement de réduction/d'agrandissement

Vous pouvez fournir plusieurs paramètres pour enterAlwaysScrollBehavior afin de personnaliser l'effet d'animation de réduction/d'expansion. TopAppBarDefaults fournit également d'autres TopAppBarScrollBehavior, comme exitUntilCollapsedScrollBehavior, qui n'étend la barre d'application que lorsque le contenu est entièrement défilé vers le bas.

Pour créer un effet entièrement personnalisé (par exemple, un effet de parallaxe), vous pouvez également créer votre propre NestedScrollConnection et décaler manuellement la barre d'outils lorsque le contenu défile. Pour obtenir un exemple de code, consultez l'exemple de défilement imbriqué sur AOSP.

Panneaux

Avec les vues, vous implémentez un panneau de navigation à l'aide d'un DrawerLayout comme vue racine. À son tour, votre CoordinatorLayout est une vue enfant de la DrawerLayout. DrawerLayout contient également une autre vue enfant, telle qu'un NavigationView, pour afficher les options de navigation dans le panneau.

Dans Compose, vous pouvez implémenter un panneau de navigation à l'aide du composable ModalNavigationDrawer. ModalNavigationDrawer propose un emplacement drawerContent pour le panneau de navigation et un emplacement content pour le contenu de l'écran.

ModalNavigationDrawer(
    drawerContent = {
        ModalDrawerSheet {
            Text("Drawer title", modifier = Modifier.padding(16.dp))
            HorizontalDivider()
            NavigationDrawerItem(
                label = { Text(text = "Drawer Item") },
                selected = false,
                onClick = { /*TODO*/ }
            )
            // ...other drawer items
        }
    }
) {
    Scaffold(Modifier.fillMaxSize()) { contentPadding ->
        // Scaffold content
        // ...
    }
}

Pour en savoir plus, consultez la section Tiroirs.

Snackbars

Scaffold fournit un emplacement snackbarHost, qui peut accepter un composable SnackbarHost pour afficher un Snackbar.

val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
    snackbarHost = {
        SnackbarHost(hostState = snackbarHostState)
    },
    floatingActionButton = {
        ExtendedFloatingActionButton(
            text = { Text("Show snackbar") },
            icon = { Icon(Icons.Filled.Image, contentDescription = "") },
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar("Snackbar")
                }
            }
        )
    }
) { contentPadding ->
    // Screen content
    // ...
}

Pour en savoir plus, consultez la section Barres de notifications.

En savoir plus

Pour en savoir plus sur la migration d'un CoordinatorLayout vers Compose, consultez les ressources suivantes: