Compose 自定义组件:封装一个通用标题栏

在 Android 开发中,标题栏(TopBar)可以说是几乎每个页面都会用到的基础组件。尽管 Material Design3 官方提供了 TopAppBar,但往往难以满足国内复杂的 UI 视觉规范(比如严格居中、动态的右侧操作按钮、不同的背景和字体颜色等)。

在 Jetpack Compose 这个声明式 UI 框架中,基于插槽(Slot API)和高阶函数,封装一个极具扩展性且高复用的自定义标题栏变得非常简单。

1. 需求分析:一个合格的 TopBar 需要什么?

在一个标准的 App 中,标题栏通常由三部分组成:

  • 左侧:返回按钮(有时不需要展示)。
  • 中间:页面标题文字。
  • 右侧:扩展功能区(可能是文本"提交/保存"、可能是搜索图标、也可能是分享/收藏等)。
  • 样式:灵活的背景颜色和标题颜色适配深浅色模式或主题色。

基于此,可以将自定义组件的参数设计为:

Kotlin 复制代码
@Composable
fun CommonTopBar(
    title: String,                               // 必填:标题文字
    onBackClick: (() -> Unit)? = null,           // 选填:返回点击事件,传 null 则不显示返回键
    rightContent: (@Composable () -> Unit)? = null, // 选填:右侧自定义内容(插槽)
    backgroundColor: Color = Color.White,        // 选填:背景色
    titleColor: Color = Color.Black              // 选填:文字颜色
)

2. 核心实现:声明式 UI 的魅力

看看在 Compose 中实现这样一个通用的头部组件有多简单。使用 Row 作为外层容器,并借助 Arrangement.SpaceBetween 将左中右三部分拉开。

Kotlin 复制代码
@Composable
fun CommonTopBar(...) {
    // 标题栏整体行布局
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .height(56.dp) // 标准标题栏高度
            .background(backgroundColor),
        verticalAlignment = Alignment.CenterVertically, // 垂直居中
        horizontalArrangement = Arrangement.SpaceBetween // 左右分散排列
    ) {
        // 1. 左侧:返回按钮区域
        Box(
            modifier = Modifier.padding(horizontal = 16.dp),
            contentAlignment = Alignment.Center
        ) {
            onBackClick?.let {
                Icon(
                    imageVector = Icons.Default.ArrowBack,
                    contentDescription = "返回",
                    tint = titleColor,
                    modifier = Modifier
                        .clickable { onBackClick() }
                        .padding(8.dp) // 增加点击热区
                )
            }
        }

        // 2. 中间:标题文字
        Text(
            text = title,
            fontSize = 18.sp,
            fontWeight = FontWeight.Bold,
            color = titleColor,
            maxLines = 1
        )

        // 3. 右侧:自定义扩展内容区
        Box(
            modifier = Modifier.padding(horizontal = 16.dp),
            contentAlignment = Alignment.Center
        ) {
            // 调用传入的 Composable 函数
            rightContent?.invoke()
        }
    }
}

技术亮点解析:

  1. 灵活的显隐控制 ( onBackClick?.let) :
    在 Compose 中,不再需要 View.VISIBLE 或 View.GONE。直接利用 Kotlin 的 null 安全特性,当传入的 onBackClick 函数不为空时,才渲染返回按钮。
  2. Slot API (插槽设计) ( rightContent: @Composable () -> Unit) :
    这是 Compose 组件封装的灵魂!没有把右侧的内容写死成某种类型(比如写死只能传 Icon 或 String),而是接收一个 @Composable 的高阶函数。这意味着使用这个组件的人,想在右边塞什么都可以:文字、图片、甚至是进度条,极大地提升了组件的复用率。
  3. 增大点击热区 ( padding和 clickable的顺序) :
    在 Modifier 中,修饰符的调用顺序非常重要。.clickable {}.padding(8.dp) 意味着先绑定点击事件,再内缩 8dp。这样在视觉上图标较小,但用户点击图标周围 8dp 范围内的空白处依然能触发点击,极大地改善了用户的交互体验。

3. 实战演示:一套代码,千变万化

有了这个灵活的 CommonTopBar 组件,可以轻松应对各种业务场景的页面头部:

场景 1:最简标题栏(无返回键)

适用于 App 的主页:

Kotlin 复制代码
CommonTopBar(title = "我是标题")

场景 2:普通带返回键的标题栏

只需要传入点击事件,返回按钮就会自动出现:

Kotlin 复制代码
CommonTopBar(
    title = "我是标题",
    onBackClick = { /* 执行返回逻辑 */ }
)

场景 3:右侧带文字操作按钮

适合表单填写页面:

Kotlin 复制代码
CommonTopBar(
    title = "我是标题",
    onBackClick = { /* 返回 */ },
    rightContent = {
        Text("提交", color = Color.Blue, modifier = Modifier.clickable { /* 提交 */ })
    }
)

场景 4:右侧带图标(如搜索或收藏)

适合资讯详情页或列表页:

Kotlin 复制代码
CommonTopBar(
    title = "文章详情",
    onBackClick = { /* 返回 */ },
    rightContent = {
        Icon(
            imageVector = Icons.Default.Favorite, 
            contentDescription = "收藏", 
            tint = Color.Red
        )
    }
)

场景 5:完全自定义的主题颜色

某些特定页面可能需要红色沉浸式背景搭配白色字体:

Kotlin 复制代码
CommonTopBar(
    title = "我是标题",
    onBackClick = { /* 返回 */ },
    backgroundColor = Color.Red,
    titleColor = Color.White
)

总结

在传统的 Android View 系统中,为了实现这么一个高度定制化的标题栏,往往需要写冗长的 XML 布局,使用各种 RelativeLayout 约束,还要在 Activity 中通过 findViewById 动态控制各种 View.GONE。

而在 Jetpack Compose 中,得益于声明式 UI 和 Kotlin 强大的 ** 高阶函数(Slot API**支持,不到 60 行代码,我们就构建出了一个极其优雅、灵活且可复用的 CommonTopBar 组件。这也是现代 Android 开发的魅力所在:让 UI 回归逻辑,让封装变得轻盈!

相关推荐
三少爷的鞋14 小时前
AI 时代,我们都将成为通才型开发者:只懂 Android,已经不够了
android
Dovis(誓平步青云)18 小时前
家里设备越来越多,如何用一张空间地图控制灯光和温度![
android·java·前端·javascript·人工智能·电脑
晚风叙码20 小时前
MySQL 数据类型详解:从数值到字符串,一篇讲透
android·mysql·adb
传奇开心果编程20 小时前
【Compose Multiplatform 跨端开发学与练】第3课 布局与组件
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓1 天前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程1 天前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer