CoordinatorLayout zu Compose migrieren

CoordinatorLayout ist ein ViewGroup, das komplexe, sich überschneidende und verschachtelte Layouts ermöglicht. Sie wird als Container verwendet, um bestimmte Material Design-Interaktionen für die darin enthaltenen Ansichten zu ermöglichen, z. B. das Ein- und Ausblenden von Symbolleisten und Bottom Sheets.

In Compose ist das CoordinatorLayout am ehesten mit einem Scaffold vergleichbar. Ein Scaffold bietet Inhalts-Slots zum Kombinieren von Material-Komponenten in gängigen Bildschirmmustern und Interaktionen. Auf dieser Seite wird beschrieben, wie Sie Ihre CoordinatorLayout-Implementierung migrieren, um Scaffold in Compose zu verwenden.

Migrationsschritte

So migrieren Sie CoordinatorLayout zu Scaffold:

  1. Im Snippet unten enthält das CoordinatorLayout ein AppBarLayout mit einem ToolBar, einem ViewPager und einem FloatingActionButton. Kommentieren Sie das CoordinatorLayout und seine untergeordneten Elemente in der UI-Hierarchie aus und fügen Sie stattdessen ein ComposeView ein.

    <!--  <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. Rufen Sie in Ihrem Fragment oder Ihrer Aktivität einen Verweis auf das ComposeView ab, das Sie gerade hinzugefügt haben, und rufen Sie die Methode setContent dafür auf. Legen Sie im Methodenkörper ein Scaffold als Inhalt fest:

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

  3. Fügen Sie den primären Inhalt Ihres Bildschirms in den Inhalt von Scaffold ein. Da der primäre Inhalt im XML oben ein ViewPager2 ist, verwenden wir ein HorizontalPager, das dem Compose-Äquivalent entspricht. Die content-Lambda-Funktion des Scaffold erhält auch eine Instanz von PaddingValues, die auf den Inhaltsstamm angewendet werden soll. Mit Modifier.padding können Sie dieselbe PaddingValues auf die HorizontalPager anwenden.

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

  4. Verwenden Sie andere Inhalts-Slots, die Scaffold bereitstellt, um weitere Bildschirmelemente hinzuzufügen und die verbleibenden untergeordneten Ansichten zu migrieren. Mit dem Slot topBar können Sie ein TopAppBar hinzufügen und mit dem Slot floatingActionButton ein 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 */ }
        }
    }

Häufige Anwendungsfälle

Symbolleisten minimieren und maximieren

Im Ansichtssystem verwenden Sie ein AppBarLayout als Container für die Symbolleiste, um die Symbolleiste mit CoordinatorLayout zu minimieren und zu maximieren. Anschließend können Sie über layout_behavior in XML für die zugehörige scrollbare Ansicht (z. B. RecyclerView oder NestedScrollView) eine Behavior angeben, um festzulegen, wie die Symbolleiste beim Scrollen ein- und ausgeblendet wird.

In Compose können Sie einen ähnlichen Effekt mit einem TopAppBarScrollBehavior erzielen. Wenn Sie beispielsweise eine ein- und ausblendbare Symbolleiste implementieren möchten, die beim Scrollen nach oben angezeigt wird, gehen Sie so vor:

  1. Rufen Sie TopAppBarDefaults.enterAlwaysScrollBehavior() auf, um ein TopAppBarScrollBehavior zu erstellen.
  2. Geben Sie die erstellte TopAppBarScrollBehavior für TopAppBar an.
  3. Verbinden Sie NestedScrollConnection über Modifier.nestedScroll auf der Scaffold, damit das Scaffold verschachtelte Scroll-Ereignisse empfangen kann, wenn der scrollbare Inhalt nach oben oder unten gescrollt wird. So kann die enthaltene App-Leiste beim Scrollen des Inhalts entsprechend ein- und ausgeblendet werden.

    // 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 */
        // ...
    }

Ein- und Ausblenden-Scrolleffekt anpassen

Sie können mehrere Parameter für enterAlwaysScrollBehavior angeben, um den Ein- und Ausblendungseffekt anzupassen. TopAppBarDefaults bietet auch andere TopAppBarScrollBehavior wie exitUntilCollapsedScrollBehavior, die die App-Leiste nur dann maximiert, wenn der Inhalt ganz nach unten gescrollt wird.

Wenn Sie einen vollständig benutzerdefinierten Effekt erstellen möchten, z. B. einen Parallaxeneffekt, können Sie auch ein eigenes NestedScrollConnection erstellen und die Symbolleiste manuell versetzen, während der Inhalt gescrollt wird. Ein Codebeispiel finden Sie im Beispiel für verschachteltes Scrollen auf AOSP.

Schubladen

Mit Views implementieren Sie eine Navigationsleiste mit einem DrawerLayout als Stammansicht. Ihre CoordinatorLayout ist wiederum eine untergeordnete Ansicht der DrawerLayout. Das DrawerLayout enthält auch eine weitere untergeordnete Ansicht, z. B. ein NavigationView, um die Navigationsoptionen in der Seitenleiste anzuzeigen.

In Compose können Sie eine Navigationsleiste mit der Composable-Funktion ModalNavigationDrawer implementieren. ModalNavigationDrawer bietet einen drawerContent-Slot für die Schublade und einen content-Slot für den Inhalt des Displays.

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

Weitere Informationen finden Sie unter Drawers.

Snackbars

Scaffold bietet einen snackbarHost-Slot, in dem ein SnackbarHost-Composable platziert werden kann, um eine Snackbar anzuzeigen.

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

Weitere Informationen finden Sie unter Snackbars.

Weitere Informationen

Weitere Informationen zur Migration einer CoordinatorLayout zu Compose finden Sie in den folgenden Ressourcen: