将 CoordinatorLayout 迁移到 Compose

CoordinatorLayout 是一种 ViewGroup,可实现复杂的重叠式嵌套布局。它用作容器,以针对其中包含的视图启用特定的 Material Design 互动,例如展开/收起工具栏和底部工作表。

在 Compose 中,最接近 CoordinatorLayout 的等效项是 Scaffold。Scaffold 提供内容槽,用于将 Material 组件组合成常见的屏幕图案和互动。本页介绍了如何迁移 CoordinatorLayout 实现以在 Compose 中使用 Scaffold。

迁移步骤

如需将 CoordinatorLayout 迁移到 Scaffold,请按以下步骤操作:

  1. 在下面的代码段中,CoordinatorLayout 包含一个用于包含 ToolBar、ViewPager 和 FloatingActionButton 的 AppBarLayout。从界面层次结构中注释掉 CoordinatorLayout 及其子项,并添加 ComposeView 来替换它。

    ```xml

    <!-- AppBarLayout contents here -->
    

    ```

  2. 在您的 fragment 或 activity 中,获取对您刚刚添加的 ComposeView 的引用,并对其调用 setContent 方法。在该方法的主体中,将 Scaffold 设置为相应内容:

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

  3. 在 Scaffold 的内容中,添加界面的主要内容。由于上述 XML 中的主要内容是 ViewPager2,因此我们将使用 HorizontalPager,它是 Compose 中与 ViewPager2 等效的元素。Scaffold 的 content lambda 还会接收一个 PaddingValues 实例,该实例应应用于内容根目录。您可以使用 Modifier.padding 将相同的 PaddingValues 应用到 HorizontalPager。

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

  4. 使用 Scaffold 提供的其他内容 slot 添加更多屏幕元素,并迁移剩余的子视图。您可以使用 topBar slot 添加 TopAppBar,并使用 floatingActionButton slot 提供 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 */ }
        }
    }

常见应用场景

收起和展开工具栏

在视图系统中,如需使用 CoordinatorLayout 收起和展开工具栏,您可以使用 AppBarLayout 作为工具栏的容器。然后,您可以在关联的可滚动视图(例如 RecyclerView 或 NestedScrollView)的 XML 中指定 Behavior 到 layout_behavior,以声明工具栏在滚动时如何折叠/展开。

在 Compose 中,您可以通过 TopAppBarScrollBehavior 实现类似的效果。例如,如需实现可折叠/展开的工具栏,以便在向上滚动时显示工具栏,请按以下步骤操作:

  1. 调用 TopAppBarDefaults.enterAlwaysScrollBehavior() 以创建 TopAppBarScrollBehavior。
  2. 向 TopAppBar 提供已创建的 TopAppBarScrollBehavior。
  3. 通过 Scaffold 上的 Modifier.nestedScroll 连接 NestedScrollConnection,以便 Scaffold 可以在可滚动内容向上/向下滚动时接收嵌套的滚动事件。这样,当内容滚动时,所含的应用栏可以相应地折叠/展开。

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

自定义折叠/展开滚动效果

您可以为 enterAlwaysScrollBehavior 提供多个参数,以自定义折叠/展开动画效果。TopAppBarDefaults 还提供了其他 TopAppBarScrollBehavior,例如 exitUntilCollapsedScrollBehavior,它仅在内容滚动到底部时展开应用栏。

如需创建完全自定义的效果(例如视差效果),您还可以创建自己的 NestedScrollConnection,并在内容滚动时手动偏移工具栏。如需查看代码示例,请参阅 AOSP 上的嵌套滚动示例。

抽屉式导航栏

使用视图时,您可以使用 DrawerLayout 作为根视图来实现抽屉式导航栏。反过来,您的 CoordinatorLayout 是 DrawerLayout 的子视图。DrawerLayout 还包含另一个子视图(例如 NavigationView),用于在抽屉式导航栏中显示导航选项。

在 Compose 中,您可以使用 ModalNavigationDrawer 可组合项实现抽屉式导航栏。ModalNavigationDrawer 为抽屉提供 drawerContent slot,为屏幕内容提供 content slot。

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

如需了解详情,请参阅抽屉。

信息提示控件

Scaffold 提供了一个 snackbarHost slot,该 slot 可接受 SnackbarHost 可组合项来显示 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
    // ...
}

如需了解详情,请参阅 Snackbar。

了解详情

如需详细了解如何将 CoordinatorLayout 迁移到 Compose,请参阅以下资源: