4、常用组件:Text、Button、Image、TextField 的使用与自定义,Icon 与图标系统。

好,咱们今天来聊聊 Jetpack Compose 里最常用、也最基础的几个组件。说实话,不管多复杂的界面,拆开来看,无非就是文字、按钮、图片、输入框和图标的排列组合。我刚开始用 Compose 的时候,觉得这玩意儿跟 XML 写 View 完全是两码事,但用顺手了之后,真香。

这一节,我会带着你把这些组件的核心用法过一遍,顺便聊聊我踩过的坑。嗯,咱们开始吧。

4.1 Text:不只是显示文字

Text 是 Compose 里最基础的组件,没有之一。你想想看,哪个 App 没有文字?但它的用法其实比你想的要灵活得多。

基本用法

@Composable
fun Greeting(name: String) {
    Text(text = "你好,$name")
}

就这么简单。但实际项目中,我们往往需要调整样式。我个人习惯把常用的文字样式封装成一个主题,而不是到处写硬编码。

自定义样式

Text(
    text = "这是一段重要提示",
    color = Color.Red,
    fontSize = 18.sp,
    fontWeight = FontWeight.Bold,
    textAlign = TextAlign.Center,
    maxLines = 2,
    overflow = TextOverflow.Ellipsis
)
小技巧: 如果你需要显示多行文本,记得设置 maxLinesoverflow,否则文字可能会把界面撑爆。我在项目中就遇到过用户输入超长文本导致布局错乱的情况,后来统一加了这两个属性,世界清净了。

富文本与 Span

有时候一段文字里需要不同的样式,比如「点击这里注册」,其中「注册」要加粗变色。这时候可以用 AnnotatedString

val annotatedString = buildAnnotatedString {
    append("点击")
    withStyle(SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
        append("注册")
    }
    append("开始使用")
}
Text(text = annotatedString)

4.2 Button:点击的学问

Button 在 Compose 里也分好几种,最常用的是 ButtonOutlinedButton。说白了,一个带背景色,一个只有边框。

基础点击

Button(onClick = { /* 处理点击 */ }) {
    Text("提交")
}

注意,Button 的内容是通过 content 槽位传入的,所以你可以放任何 Composable,不只是文字。比如放一个图标加文字:

Button(onClick = { /* 登录 */ }) {
    Icon(Icons.Default.Person, contentDescription = "用户")
    Spacer(modifier = Modifier.width(8.dp))
    Text("登录")
}
避坑指南: 我曾经在 Button 里直接放了一个 Image,结果图片把按钮撑得变形了。后来发现,给图片加上 Modifier.size(24.dp) 就好了。记住,Button 本身不会限制子组件的大小,你得自己控制。

自定义形状与颜色

Button(
    onClick = { /* 删除 */ },
    colors = ButtonDefaults.buttonColors(
        containerColor = Color.Red,
        contentColor = Color.White
    ),
    shape = RoundedCornerShape(8.dp)
) {
    Text("删除")
}

4.3 Image:图片加载与占位

Compose 里的 Image 组件,说白了就是用来显示图片的。但要注意,它本身不负责网络加载,你需要配合 Coil 或 Glide 的 Compose 扩展来用。

显示本地资源

Image(
    painter = painterResource(id = R.drawable.ic_launcher),
    contentDescription = "应用图标",
    modifier = Modifier.size(64.dp)
)

显示网络图片(使用 Coil)

AsyncImage(
    model = "https://example.com/image.jpg",
    contentDescription = "网络图片",
    placeholder = painterResource(R.drawable.placeholder),
    error = painterResource(R.drawable.error)
)

嗯,这里要提一句,contentDescription 是给无障碍功能用的,别偷懒不写。我见过很多 App 图片没有描述,屏幕阅读器用户根本不知道那是什么。

缩放模式

ContentScale 控制图片如何适应容器。常用的有:

  • ContentScale.Fit — 保持比例,完整显示
  • ContentScale.Crop — 裁剪填充,不留空白
  • ContentScale.FillBounds — 拉伸填充,可能变形
我的经验: 头像列表用 Crop,封面图用 Fit,产品图用 FillBounds 要谨慎,容易把商品拉变形。

4.4 TextField:输入框的进化

TextField 在 Compose 里分两种:TextField(Material 2 风格)和 OutlinedTextField(带边框)。我个人更推荐 OutlinedTextField,视觉上更清晰。

基本输入

var text by remember { mutableStateOf("") }
OutlinedTextField(
    value = text,
    onValueChange = { text = it },
    label = { Text("用户名") },
    placeholder = { Text("请输入用户名") }
)

注意,valueonValueChange 是必须的,Compose 是单向数据流,你得自己管理状态。

输入验证与键盘类型

OutlinedTextField(
    value = text,
    onValueChange = { if (it.length <= 11) text = it },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
    visualTransformation = PasswordVisualTransformation(),
    isError = text.length < 11 && text.isNotEmpty(),
    supportingText = {
        if (text.isNotEmpty() && text.length < 11) {
            Text("手机号必须为11位", color = MaterialTheme.colorScheme.error)
        }
    }
)
小提示:visualTransformation 可以实现密码遮罩、信用卡号格式化等效果。我曾经做过一个银行卡号输入框,每4位自动加空格,就是用 VisualTransformation 实现的。

4.5 Icon 与图标系统

Compose 自带了一套 Material Icons,分为 Icons.DefaultIcons.FilledIcons.Outlined 等风格。用法很简单:

Icon(
    imageVector = Icons.Default.Home,
    contentDescription = "首页",
    tint = Color.Gray
)

如果你需要自定义图标,可以用 ImageVector 或者直接放一个 Image 组件加载 SVG。我个人习惯把常用的图标封装成一个 sealed class,方便统一管理。

图标与文字组合

Row(verticalAlignment = Alignment.CenterVertically) {
    Icon(Icons.Default.Star, contentDescription = null, modifier = Modifier.size(16.dp))
    Spacer(modifier = Modifier.width(4.dp))
    Text("4.5")
}

这里 contentDescription 设为 null 表示这个图标是装饰性的,屏幕阅读器会忽略它。嗯,这个细节很多人不注意。

知识体系总览

下面这张图,是我梳理的本章核心组件关系。你看一眼,心里就有谱了。

Jetpack Compose 常用组件 Text 样式、富文本、溢出 Button 点击、形状、颜色 Image 本地、网络、缩放 TextField 输入、验证、键盘 Icon 与图标系统 Material Icons、自定义、组合 核心:状态管理 + 样式定制 + 无障碍

自定义组件:组合的艺术

Compose 的核心思想就是「组合优于继承」。你不需要创建一个自定义 View,只需要把现有组件拼起来。比如,一个带图标的输入框:

@Composable
fun IconTextField(
    value: String,
    onValueChange: (String) -> Unit,
    icon: ImageVector,
    label: String
) {
    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        label = { Text(label) },
        leadingIcon = { Icon(icon, contentDescription = null) },
        singleLine = true
    )
}

你看,就这么几行代码,一个可复用的组件就出来了。我在项目中经常这样封装,把重复的 UI 模式抽象成函数,代码量直接减半。

总结一下: Text、Button、Image、TextField、Icon 这五个组件,是 Compose 世界的基石。掌握它们的用法和自定义方式,你就能搭建出 90% 的常见界面。别怕写代码,多试几次,手感就来了。

公众号:蓝海资料掘金营,微信deep3321