首页 / Android 入门教程 / Compose 卡片与容器

Android 入门教程

Compose 卡片与容器

本教程共 100 篇 · 第 49 篇 · 更新于 2026-07-28 · 约 8 分钟阅读

AndroidAndroid 入门教程Jetpack ComposeCardScaffoldTopAppBarSurface

49. Compose 卡片与容器

本节目标:学会用 Card 做卡片、Surface 做表面容器、Scaffold 搭建页面骨架(顶栏、底栏、FAB),以及用 BottomSheet 做底部弹层。

Card 卡片

Card 是带阴影和圆角的容器,适合做列表项、信息块:

@Composable
fun ArticleCard(title: String, summary: String) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        shape = RoundedCornerShape(12.dp)
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                title,
                style = MaterialTheme.typography.titleMedium,
                fontWeight = FontWeight.Bold
            )
            Spacer(Modifier.height(8.dp))
            Text(
                summary,
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                maxLines = 2,
                overflow = TextOverflow.Ellipsis
            )
        }
    }
}
参数作用
elevation阴影高度(z 轴)
shape圆角形状
colors背景和内容颜色
Note
  • Card 本质是一个带阴影和圆角的 Surface。它适合做需要视觉「浮起」效果的内容块。如果你只需要圆角和背景色,不一定非要用 Card,用 Modifier.clip() + background() 就行。

Surface 表面

Surface 是更底层的容器,Card 其实是基于 Surface 实现的。Surface 提供背景色、阴影、裁剪、内容颜色:

Surface(
    color = MaterialTheme.colorScheme.primaryContainer,
    shape = RoundedCornerShape(8.dp),
    modifier = Modifier.padding(8.dp)
) {
    Text(
        "标签",
        modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
        color = MaterialTheme.colorScheme.onPrimaryContainer
    )
}
Tip
  • Surface 的 color 会自动应用到子内容的默认颜色(比如文字色)。设了 primaryContainer 背景,子文字默认就是 onPrimaryContainer 色。这是 Surface 比 Box 好的地方。

Scaffold 脚手架

Scaffold 是 Material Design 的页面骨架,帮你组合顶栏、底栏、FAB、抽屉等标准结构:

@Composable
fun MainScreen() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("首页") },
                navigationIcon = {
                    IconButton(onClick = { }) {
                        Icon(Icons.Default.Menu, contentDescription = "菜单")
                    }
                },
                actions = {
                    IconButton(onClick = { }) {
                        Icon(Icons.Default.Search, contentDescription = "搜索")
                    }
                }
            )
        },
        bottomBar = {
            NavigationBar {
                items.forEach { item ->
                    NavigationBarItem(
                        selected = selectedItem == item,
                        onClick = { selectedItem = item },
                        icon = { Icon(item.icon, contentDescription = item.label) },
                        label = { Text(item.label) }
                    )
                }
            }
        },
        floatingActionButton = {
            FloatingActionButton(onClick = { }) {
                Icon(Icons.Default.Add, contentDescription = "新建")
            }
        }
    ) { paddingValues ->
        // paddingValues 是 Scaffold 算好的内边距,避免内容被顶栏/底栏遮挡
        ContentList(modifier = Modifier.padding(paddingValues))
    }
}

Scaffold 的参数都是可选的,按需用:

参数作用
topBar顶部应用栏
bottomBar底部导航栏
floatingActionButton悬浮按钮
snackbarHostSnackbar 宿主
drawerContent侧滑抽屉内容
Warning
  • Scaffold 的 content lambda 里有 paddingValues 参数。一定要用这个 padding,它根据顶栏底栏的高度自动计算,不用它内容会被遮挡。

TopAppBar 顶部栏

TopAppBar(
    title = { Text("标题") },
    navigationIcon = {
        IconButton(onClick = { /* 返回 */ }) {
            Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "返回")
        }
    },
    actions = {
        IconButton(onClick = { }) {
            Icon(Icons.Default.Share, contentDescription = "分享")
        }
        IconButton(onClick = { }) {
            Icon(Icons.Default.MoreVert, contentDescription = "更多")
        }
    },
    colors = TopAppBarDefaults.topAppBarColors(
        containerColor = MaterialTheme.colorScheme.primaryContainer,
        titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer
    )
)
  • navigationIcon:左侧导航按钮(返回、菜单)
  • actions:右侧操作按钮(搜索、分享、更多)
Note
  • CenterAlignedTopAppBar 让标题居中,LargeTopAppBar 提供可折叠的大标题。根据设计选。
data class NavItem(val label: String, val icon: ImageVector)

@Composable
fun BottomNav() {
    val items = listOf(
        NavItem("首页", Icons.Default.Home),
        NavItem("搜索", Icons.Default.Search),
        NavItem("我的", Icons.Default.Person)
    )
    var current by remember { mutableStateOf(0) }

    NavigationBar {
        items.forEachIndexed { index, item ->
            NavigationBarItem(
                selected = current == index,
                onClick = { current = index },
                icon = { Icon(item.icon, contentDescription = item.label) },
                label = { Text(item.label) }
            )
        }
    }
}

ModalBottomSheet 底部弹层

底部弹出的面板,适合做选项菜单、表单:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetExample() {
    var showSheet by remember { mutableStateOf(false) }

    Button(onClick = { showSheet = true }) {
        Text("打开选项")
    }

    if (showSheet) {
        ModalBottomSheet(
            onDismissRequest = { showSheet = false }
        ) {
            Column(modifier = Modifier.padding(16.dp)) {
                Text("选项一", modifier = Modifier
                    .fillMaxWidth()
                    .clickable { showSheet = false }
                    .padding(12.dp))
                Text("选项二", modifier = Modifier
                    .fillMaxWidth()
                    .clickable { showSheet = false }
                    .padding(12.dp))
            }
        }
    }
}
Tip
  • onDismissRequest 在用户下滑关闭或点遮罩时调用,记得在这里隐藏 Sheet。底部弹层比 Dialog 更适合移动端操作,手指够得到。

Snackbar 提示条

Scaffold 自带 Snackbar 支持:

@Composable
fun SnackbarExample() {
    val snackbarHostState = remember { SnackbarHostState() }
    val scope = rememberCoroutineScope()

    Scaffold(
        snackbarHost = { SnackbarHost(snackbarHostState) }
    ) { padding ->
        Button(
            onClick = {
                scope.launch {
                    snackbarHostState.showSnackbar("已保存")
                }
            }
        ) {
            Text("保存")
        }
    }
}

小结

Card 做信息卡片,Surface 做底层表面容器。Scaffold 是页面骨架,帮你组合顶栏、底栏、FAB、Snackbar,别忘了用 paddingValues。TopAppBar、NavigationBar、BottomSheet 是最常用的三个结构组件。下一节学习主题和样式。