Memigrasikan CoordinatorLayout ke Compose

CoordinatorLayout adalah ViewGroup yang memungkinkan tata letak yang kompleks, tumpang-tindih, dan bertingkat. View ini digunakan sebagai penampung untuk mengaktifkan interaksi Desain Material tertentu, seperti meluaskan/menyempitkan toolbar dan sheet bawah, untuk View yang terdapat di dalamnya.

Di Compose, yang setara dengan CoordinatorLayout adalah Scaffold. Scaffold menyediakan slot konten untuk menggabungkan Komponen Material ke dalam pola dan interaksi layar yang umum. Halaman ini menjelaskan cara memigrasikan implementasi CoordinatorLayout untuk menggunakan Scaffold di Compose.

Langkah migrasi

Untuk memigrasikan CoordinatorLayout ke Scaffold, ikuti langkah-langkah berikut:

  1. Dalam cuplikan di bawah, CoordinatorLayout berisi AppBarLayout untuk berisi ToolBar, ViewPager, dan FloatingActionButton. Beri komentar pada CoordinatorLayout dan turunannya dari hierarki UI Anda, lalu tambahkan ComposeView untuk menggantinya.

    <!--  <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. Di Fragmen atau Aktivitas, dapatkan referensi ke ComposeView yang baru saja Anda tambahkan dan panggil metode setContent di dalamnya. Dalam isi metode, tetapkan Scaffold sebagai kontennya:

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

  3. Dalam konten Scaffold, tambahkan konten utama layar dalam konten tersebut. Karena konten utama dalam XML di atas adalah ViewPager2, kita akan menggunakan HorizontalPager, yang merupakan Compose yang setara. Lambda content dari Scaffold juga menerima instance PaddingValues yang harus diterapkan ke root konten. Anda dapat menggunakan Modifier.padding untuk menerapkan PaddingValues yang sama ke HorizontalPager.

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

  4. Gunakan slot konten lain yang disediakan Scaffold untuk menambahkan lebih banyak elemen layar dan memigrasikan View turunan yang tersisa. Anda dapat menggunakan slot topBar untuk menambahkan TopAppBar, dan slot floatingActionButton untuk memberikan 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 */ }
        }
    }

Kasus penggunaan umum

Menciutkan dan meluaskan toolbar

Dalam sistem View, untuk menciutkan dan meluaskan toolbar dengan CoordinatorLayout, Anda menggunakan AppBarLayout sebagai penampung untuk toolbar. Kemudian, Anda dapat menentukan Behavior melalui layout_behavior dalam XML pada View yang dapat di-scroll terkait (seperti RecyclerView atau NestedScrollView) untuk mendeklarasikan cara toolbar ditutup/diperluas saat Anda men-scroll.

Di Compose, Anda dapat memperoleh efek yang serupa melalui TopAppBarScrollBehavior. Misalnya, untuk menerapkan toolbar yang diciutkan/diperluas sehingga toolbar muncul saat Anda men-scroll ke atas, ikuti langkah-langkah berikut:

  1. Panggil TopAppBarDefaults.enterAlwaysScrollBehavior() untuk membuat TopAppBarScrollBehavior.
  2. Berikan TopAppBarScrollBehavior yang dibuat ke TopAppBar.
  3. Hubungkan NestedScrollConnection melalui Modifier.nestedScroll di Scaffold sehingga Scaffold dapat menerima peristiwa scroll bertingkat saat konten yang dapat di-scroll di-scroll ke atas/bawah. Dengan cara ini, panel aplikasi yang dimuat dapat ditutup/diperluas dengan tepat saat konten di-scroll.

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

Menyesuaikan efek scroll yang diciutkan/diperluas

Anda dapat memberikan beberapa parameter untuk enterAlwaysScrollBehavior guna menyesuaikan efek animasi penyingkatan/perluasan. TopAppBarDefaults juga menyediakan TopAppBarScrollBehavior lain seperti exitUntilCollapsedScrollBehavior, yang hanya meluaskan panel aplikasi saat konten di-scroll ke bawah.

Untuk membuat efek yang sepenuhnya kustom (misalnya, efek paralaks), Anda juga dapat membuat NestedScrollConnection Anda sendiri dan mengimbangi toolbar secara manual saat konten di-scroll. Lihat Contoh scroll bertingkat di AOSP untuk contoh kode.

Panel samping

Dengan View, Anda mengimplementasikan panel navigasi menggunakan DrawerLayout sebagai tampilan root. Pada gilirannya, CoordinatorLayout Anda adalah tampilan turunan dari DrawerLayout. DrawerLayout juga berisi tampilan anak lain, seperti NavigationView, untuk menampilkan opsi navigasi di panel samping.

Di Compose, Anda dapat menerapkan panel navigasi menggunakan composable ModalNavigationDrawer. ModalNavigationDrawer menawarkan slot drawerContent untuk panel samping dan slot content untuk konten layar.

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

Lihat Panel samping untuk mempelajari lebih lanjut.

Snackbar

Scaffold menyediakan slot snackbarHost, yang dapat menerima composable SnackbarHost untuk menampilkan 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
    // ...
}

Lihat Snackbar untuk mempelajari lebih lanjut.

Pelajari lebih lanjut

Untuk informasi selengkapnya tentang memigrasikan CoordinatorLayout ke Compose, lihat referensi berikut: