Di chuyển CoordinatorLayout sang Compose

CoordinatorLayout là một ViewGroup cho phép bố cục phức tạp, chồng chéo và lồng nhau. Đây là vùng chứa cho phép các hoạt động tương tác cụ thể của Material Design (chẳng hạn như mở rộng/thu gọn thanh công cụ và bảng dưới cùng) cho các Thành phần hiển thị có trong đó.

Trong Compose, thành phần tương đương gần nhất với CoordinatorLayoutScaffold. Scaffold cung cấp các khe nội dung để kết hợp các Thành phần Material thành các mẫu màn hình và lượt tương tác phổ biến. Trang này mô tả cách bạn có thể di chuyển việc triển khai CoordinatorLayout để sử dụng Scaffold trong Compose.

Các bước di chuyển

Để di chuyển CoordinatorLayout sang Scaffold, hãy làm theo các bước sau:

  1. Trong đoạn mã dưới đây, CoordinatorLayout chứa một AppBarLayout để chứa ToolBar, ViewPagerFloatingActionButton. Nhận xét CoordinatorLayout và các thành phần con của thành phần này trong hệ phân cấp giao diện người dùng của bạn, đồng thời thêm ComposeView để thay thế.

    <!--  <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. Trong Mảnh hoặc Hoạt động của bạn, hãy lấy một tham chiếu đến ComposeView mà bạn vừa thêm và gọi phương thức setContent trên tham chiếu đó. Trong phần nội dung của phương thức, hãy đặt Scaffold làm nội dung:

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

  3. Trong nội dung của Scaffold, hãy thêm nội dung chính của màn hình vào bên trong. Vì nội dung chính trong XML ở trên là ViewPager2, nên chúng ta sẽ sử dụng HorizontalPager, đây là thành phần tương đương của Compose. Hàm lambda content của Scaffold cũng nhận được một thực thể PaddingValues sẽ được áp dụng cho nội dung gốc. Bạn có thể dùng Modifier.padding để áp dụng cùng một PaddingValues cho HorizontalPager.

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

  4. Sử dụng các vị trí nội dung khác mà Scaffold cung cấp để thêm nhiều phần tử màn hình hơn và di chuyển các Khung hiển thị con còn lại. Bạn có thể sử dụng khe topBar để thêm TopAppBar và khe floatingActionButton để cung cấp 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 */ }
        }
    }

Các trường hợp sử dụng phổ biến

Thu gọn và mở rộng thanh công cụ

Trong hệ thống View, để thu gọn và mở rộng thanh công cụ bằng CoordinatorLayout, bạn sẽ dùng AppBarLayout làm vùng chứa cho thanh công cụ. Sau đó, bạn có thể chỉ định một Behavior thông qua layout_behavior trong XML trên View có thể cuộn được liên kết (chẳng hạn như RecyclerView hoặc NestedScrollView) để khai báo cách thanh công cụ thu gọn/mở rộng khi bạn cuộn.

Trong Compose, bạn có thể đạt được hiệu ứng tương tự thông qua TopAppBarScrollBehavior. Ví dụ: để triển khai một thanh công cụ thu gọn/mở rộng để thanh công cụ xuất hiện khi bạn cuộn lên, hãy làm theo các bước sau:

  1. Gọi TopAppBarDefaults.enterAlwaysScrollBehavior() để tạo TopAppBarScrollBehavior.
  2. Cung cấp TopAppBarScrollBehavior đã tạo cho TopAppBar.
  3. Kết nối NestedScrollConnection thông qua Modifier.nestedScroll trên Scaffold để Scaffold có thể nhận các sự kiện cuộn lồng khi nội dung có thể cuộn di chuyển lên/xuống. Bằng cách này, thanh ứng dụng được chứa có thể thu gọn/mở rộng một cách thích hợp khi nội dung cuộn.

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

Tuỳ chỉnh hiệu ứng thu gọn/mở rộng khi di chuyển

Bạn có thể cung cấp một số tham số cho enterAlwaysScrollBehavior để tuỳ chỉnh hiệu ứng ảnh động thu gọn/mở rộng. TopAppBarDefaults cũng cung cấp các TopAppBarScrollBehavior khác, chẳng hạn như exitUntilCollapsedScrollBehavior. Thành phần này chỉ mở rộng thanh ứng dụng khi nội dung được cuộn xuống dưới cùng.

Để tạo một hiệu ứng hoàn toàn tuỳ chỉnh (ví dụ: hiệu ứng thị sai), bạn cũng có thể tạo NestedScrollConnection của riêng mình và bù đắp thanh công cụ theo cách thủ công khi nội dung cuộn. Hãy xem Mẫu cuộn lồng nhau trên AOSP để biết ví dụ về mã.

Ngăn kéo

Với Khung hiển thị, bạn triển khai một ngăn điều hướng bằng cách sử dụng DrawerLayout làm khung hiển thị gốc. Đến lượt, CoordinatorLayout của bạn là thành phần hiển thị con của DrawerLayout. DrawerLayout cũng chứa một khung hiển thị con khác, chẳng hạn như NavigationView, để hiển thị các lựa chọn điều hướng trong ngăn kéo.

Trong Compose, bạn có thể triển khai một ngăn điều hướng bằng thành phần kết hợp ModalNavigationDrawer. ModalNavigationDrawer cung cấp một khe drawerContent cho ngăn và một khe content cho nội dung của màn hình.

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

Hãy xem phần Ngăn kéo để tìm hiểu thêm.

Thanh thông báo nhanh

Scaffold cung cấp một vị trí snackbarHost có thể chấp nhận thành phần kết hợp SnackbarHost để hiển thị 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
    // ...
}

Hãy xem phần Thông báo dạng thanh để tìm hiểu thêm.

Tìm hiểu thêm

Để biết thêm thông tin về cách di chuyển một CoordinatorLayout sang Compose, hãy xem các tài nguyên sau: