Jetpack系列之Compose Scaffold

(一)概述

Scaffold 是 Material Design 提供的一个页面骨架组件,用于统一管理:TopAppBar(顶部栏),BottomBar(底部栏),FloatingActionButton(悬浮按钮),Snackbar,Drawer(侧边栏),主内容区(Content)。可以把 Scaffold 理解为:Compose 页面级布局的"地基"。Scaffold可以自动处理 Insets / padding,统一了页面结构, 与 TopAppBar / SnackbarHost 深度配合。总之,Scaffold 是 Compose 页面级布局的标准入口,负责"结构",而不是"内容"。

(二)Scaffold 属性说明

Kotlin 复制代码
@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    topBar: @Composable () -> Unit = {},
    bottomBar: @Composable () -> Unit = {},
    snackbarHost: @Composable () -> Unit = {},
    floatingActionButton: @Composable () -> Unit = {},
    floatingActionButtonPosition: FabPosition = FabPosition.End,
    containerColor: Color = MaterialTheme.colorScheme.background,
    contentColor: Color = contentColorFor(containerColor),
    contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
    content: @Composable (PaddingValues) -> Unit
) {
    // 创建一个可变的窗口inset对象
    // remember:缓存计算结果,当 contentWindowInsets 变化时重新计算
    // MutableWindowInsets:可变的窗口inset,用于动态调整inset区域
    val safeInsets = remember(contentWindowInsets) { MutableWindowInsets(contentWindowInsets) }
    // Surface:Material Design 的表面组件,提供颜色、形状等特性
    Surface(
        modifier =
            // 监听已经被消耗的窗口inset
            modifier.onConsumedWindowInsetsChanged { consumedWindowInsets ->
                // Exclude currently consumed window insets from user provided contentWindowInsets
                safeInsets.insets = contentWindowInsets.exclude(consumedWindowInsets)
            },
        // 设置表面颜色和内容颜色
        color = containerColor,
        contentColor = contentColor
    ) {
        // ScaffoldLayout:实际的布局组件,负责排列各个部分
        ScaffoldLayout(
            fabPosition = floatingActionButtonPosition,
            topBar = topBar,
            bottomBar = bottomBar,
            content = content,
            snackbar = snackbarHost,
            contentWindowInsets = safeInsets,
            fab = floatingActionButton
        )
    }
}

modifier - 修饰符,用于调整布局大小、位置等。

topBar - 顶部栏(如 AppBar),通常是 TopAppBar。

bottomBar - 底部栏(如 BottomNavigation)。

snackbarHost - Snackbar 容器,用于显示提示信息。

floatingActionButton - 悬浮操作按钮(FAB)。

floatingActionButtonPosition - FAB 位置,默认右侧,FabPosition.End - 右侧(RTL 布局时是左侧),FabPosition.Center - 居中。

containerColor - Scaffold 容器背景色,默认使用主题的背景色。

contentColor - 内容颜色,自动根据容器颜色计算对比色。

contentWindowInsets - 内容区域的窗口Inset(处理状态栏、导航栏等)。

content - 主要内容区域,接收 PaddingValues 来避免内容被遮挡。

(三)Scaffold 封装

Kotlin 复制代码
package com.leo.wechat.utils

import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import com.google.accompanist.systemuicontroller.rememberSystemUiController

@Composable
fun SystemBarController(
    backgroundColor: Color,
    contentColor: Color,
) {
    // 根据内容颜色的明暗,自动判断系统状态栏 / 导航栏图标是用深色还是浅色
    val darkIcons = contentColor.luminance() < 0.5f

    // implementation 'com.google.accompanist:accompanist-systemuicontroller:0.32.0'
    val systemUiController = rememberSystemUiController()
    SideEffect {
        systemUiController.setStatusBarColor(
            color = backgroundColor,
            darkIcons = darkIcons
        )
        systemUiController.setNavigationBarColor(
            color = backgroundColor,
            darkIcons = darkIcons
        )
    }
}

SystemBarController.kt 说明:封装系统状态栏 / 导航栏图标是用深色还是浅色
Kotlin 复制代码
package com.leo.wechat.ui.component.scaffold

import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import com.leo.wechat.ui.component.topbar.AppTopBar
import com.leo.wechat.ui.component.topbar.AppTopBarIcon
import com.leo.wechat.utils.SystemBarController

/* StatusBar / TopBar / 页面背景 自动统一 */
@Composable
fun AppScaffold(
    title: String,
    modifier: Modifier = Modifier,
    startIcon: AppTopBarIcon? = null,
    endIcon: AppTopBarIcon? = null,
    backgroundColor: Color = MaterialTheme.colorScheme.background,
    contentColor: Color = MaterialTheme.colorScheme.onBackground,
    content: @Composable (PaddingValues) -> Unit,
) {
    SystemBarController(
        backgroundColor = backgroundColor,
        contentColor = contentColor
    )

    Scaffold(
        modifier = modifier.fillMaxSize(),
        topBar = {
            AppTopBar(
                title = title,
                /**
                 * Modifier.statusBarsPadding() 的作用是:
                 * 给 Composable 自动加上"状态栏高度"的内边距,避免内容被状态栏遮挡。
                 * Modifier.statusBarsPadding() 等价于:Modifier.padding(top = statusBarHeight)
                 */
                modifier = Modifier.statusBarsPadding(),
                backgroundColor = backgroundColor,
                contentColor = contentColor,
                startIcon = startIcon,
                endIcon = endIcon
            )
        },
        containerColor = backgroundColor
    ) { innerPadding ->
        content(innerPadding)
    }
}

AppScaffold.kt 说明:支持StatusBar / TopBar / 页面背景 自动统一,支持左右Icon按钮的TopBar
Kotlin 复制代码
// 替代传统的 AppCompatActivity + XML
class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 创建 Compose UI 树,替代 setContentView(R.layout.xxx),
        // 内部是一个 Composable Scope(只要 State 变化,里面的 Composable 会自动重组)。
        setContent {
            // 控制全局主题(浅色 / 深色 等),所有 UI 都应该在 ComposeTheme 内部。
            ComposeTheme {
                // Context 只在需要它的最小 Composable 作用域中获取
                val context = LocalContext.current
                // 自定义布局架构
                AppScaffold(
                    title = "微信",
                    startIcon = AppTopBarIcons.back { finish() },
                    endIcon = AppTopBarIcons.more {
                        Toast.makeText(context, "更多", Toast.LENGTH_SHORT).show()
                    }
                ) { innerPadding ->

                    /**
                     * 在自定义 AppScaffold 或 Scaffold 中:
                     *  content 区域如果不消费 innerPadding,内容会被 layout 到 0 size 或被 top bar 覆盖。
                     */
                }
            }
        }
    }
}

MainActivity.kt 说明: 主页面,使用 Compose 替代传统的 AppCompatActivity + XML 写法。
相关推荐
爱装代码的小瓶子4 小时前
【C++与Linux基础】进程间通讯方式:匿名管道
android·c++·后端
兴趣使然HX4 小时前
Android绘帧流程解析
android
JMchen1235 小时前
Android UDP编程:实现高效实时通信的全面指南
android·经验分享·网络协议·udp·kotlin
黄林晴6 小时前
Android 17 再曝猛料:通知栏和快捷设置终于分家了,这操作等了十年
android
有位神秘人6 小时前
Android获取设备中本地音频
android·音视频
JMchen1236 小时前
Android网络安全实战:从HTTPS到双向认证
android·经验分享·网络协议·安全·web安全·https·kotlin
CS创新实验室6 小时前
Pandas 3 的新功能
android·ide·pandas
ujainu6 小时前
护眼又美观:Flutter + OpenHarmony 鸿蒙记事本一键切换夜间模式(四)
android·flutter·harmonyos
三少爷的鞋7 小时前
为什么我不在 Android ViewModel 中直接处理异常?
android
草莓熊Lotso8 小时前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能