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.VISIBLEView.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 回归逻辑,让封装变得轻盈!

相关推荐
ZHOUPUYU2 小时前
PHP性能优化实战:提升你的应用速度
android·性能优化·php
Railshiqian2 小时前
安卓源码编译ko文件到设备img,并在开机阶段自动加载
android·kernel
NoSi EFUL3 小时前
学生成绩管理系统(MySQL)
android·数据库·mysql
molong9313 小时前
SIM 卡监听(电话监听)
android·学习·kotlin
帅次4 小时前
Android 高级工程师面试参考答案:Framework、生命周期、View 与 Binder
android·面试·binder
程序员陆业聪4 小时前
AI提效Android开发全景图:从需求到上线的AI工具链
android
李白的天不白4 小时前
滚动条样式大全
android
程序员陆业聪4 小时前
你调的每一个接口背后,到底发生了什么?
android
Railshiqian4 小时前
common-android15-6.6 kernel环境下,编写并编译一个helloworld驱动模块
android·kernel