5、文本与样式:Text组件、字体与颜色、Style样式、AnnotatedString富文本、TextField输入框
文本是UI的灵魂。不管你的界面设计得多花哨,用户最终看的还是上面的字。Jetpack Compose 在文本处理上给了我不少惊喜——它把传统 Android 里那些繁琐的 SpannableString、自定义字体加载,全都浓缩成了声明式的 API。今天我们就来彻底搞懂它。
5.1 Text 组件:最基础的展示单元
Text 组件是 Compose 里最常用的组件之一。用法极其简单:
@Composable
fun SimpleText() {
Text(text = "Hello, Compose!")
}
但别被它的外表骗了。Text 背后藏着大量可配置的参数。我整理了一份常用参数表:
| 参数 | 类型 | 说明 |
|---|---|---|
| text | String / AnnotatedString | 显示的文本内容 |
| modifier | Modifier | 布局修饰符 |
| color | Color | 文字颜色 |
| fontSize | TextUnit | 字体大小(sp) |
| fontStyle | FontStyle | 斜体等 |
| fontWeight | FontWeight | 字重(粗体等) |
| fontFamily | FontFamily | 字体族 |
| letterSpacing | TextUnit | 字间距 |
| textDecoration | TextDecoration | 下划线、删除线 |
| textAlign | TextAlign | 对齐方式 |
| lineHeight | TextUnit | 行高 |
| overflow | TextOverflow | 溢出处理 |
| maxLines | Int | 最大行数 |
举个例子,一个带样式的标题:
Text(
text = "欢迎来到 Compose 世界",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1A73E8),
letterSpacing = 2.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
5.2 字体与颜色:让文字有「性格」
字体和颜色是文字风格的基石。Compose 提供了非常灵活的方式来控制它们。
5.2.1 颜色
颜色可以直接用 Color 类指定:
Text(
text = "红色警告",
color = Color.Red
)
// 使用 ARGB
Text(
text = "自定义颜色",
color = Color(0xFFFF5722)
)
更推荐的做法是使用 MaterialTheme 的颜色系统:
Text(
text = "主色调文字",
color = MaterialTheme.colorScheme.primary
)
Text(
text = "次要文字",
color = MaterialTheme.colorScheme.onSurfaceVariant
)
5.2.2 字体
Compose 默认使用系统字体。如果你想用自定义字体,需要先把它放到 res/font 目录下,然后这样加载:
// 在 res/font/ 下放置 your_font.ttf
val customFont = FontFamily(
Font(R.font.your_font, FontWeight.Normal),
Font(R.font.your_font_bold, FontWeight.Bold)
)
Text(
text = "自定义字体",
fontFamily = customFont,
fontWeight = FontWeight.Bold
)
如果你需要下载字体(比如 Google Fonts),可以用 Compose 的字体下载 API:
val fontFamily = FontFamily(
GoogleFont("Roboto"),
Font(
googleFont = GoogleFont("Roboto"),
fontProvider = GoogleFont.Provider(
providerAuthority = "com.google.android.gms.fonts",
providerPackage = "com.google.android.gms",
certificates = R.array.com_google_android_gms_fonts_certs
)
)
)
嗯,这个写法有点啰嗦。我个人更倾向于把常用的字体封装成一个全局变量,避免到处重复这段代码。
5.3 Style 样式:复用文本样式
如果你有多个 Text 组件需要相同的样式,一个个写参数就太累了。这时候就该 Style 出场了。
val titleStyle = TextStyle(
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF333333),
letterSpacing = 1.sp,
lineHeight = 28.sp
)
val bodyStyle = TextStyle(
fontSize = 14.sp,
color = Color(0xFF666666),
lineHeight = 20.sp
)
// 使用
Text(text = "标题", style = titleStyle)
Text(text = "正文内容...", style = bodyStyle)
更高级的用法是结合 MaterialTheme 的 Typography:
// 在 Theme 中定义
val typography = Typography(
titleLarge = TextStyle(
fontSize = 22.sp,
fontWeight = FontWeight.Bold
),
bodyMedium = TextStyle(
fontSize = 14.sp,
lineHeight = 20.sp
)
)
// 使用
Text(
text = "标题",
style = MaterialTheme.typography.titleLarge
)
5.4 AnnotatedString 富文本:一段文字,多种样式
有时候你需要在一段文字里混用不同的样式。比如「注意:请仔细阅读以下条款」。传统 Android 用 SpannableString,Compose 里用 AnnotatedString。
val annotatedString = buildAnnotatedString {
// 普通文字
append("注意:")
// 加粗红色文字
withStyle(
SpanStyle(
fontWeight = FontWeight.Bold,
color = Color.Red
)
) {
append("请仔细")
}
// 普通文字
append("阅读以下条款")
// 添加点击事件
pushStringAnnotation(tag = "link", annotation = "https://example.com")
withStyle(
SpanStyle(
color = Color.Blue,
textDecoration = TextDecoration.Underline
)
) {
append("点击这里")
}
pop()
}
Text(text = annotatedString)
// 处理点击
ClickableText(
text = annotatedString,
onClick = { offset ->
annotatedString.getStringAnnotations(
tag = "link",
start = offset,
end = offset
).firstOrNull()?.let { annotation ->
// 打开链接
}
}
)
这里有个细节要注意:pushStringAnnotation 和 pop 必须成对出现,否则会报错。我刚开始用的时候漏掉了 pop,结果编译通过了但运行时点击没反应,排查了半天才发现是这个问题。
5.5 TextField 输入框:让用户说话
输入框是交互的核心。Compose 提供了两个版本:TextField(Material Design 2 风格)和 OutlinedTextField(带外边框)。
5.5.1 基本用法
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("用户名") },
placeholder = { Text("请输入用户名") },
leadingIcon = {
Icon(Icons.Default.Person, contentDescription = null)
},
modifier = Modifier.fillMaxWidth()
)
注意:value 和 onValueChange 是必须的。Compose 的输入框是受控组件,数据流向是单向的——用户输入触发 onValueChange,你更新状态,状态再驱动 UI 刷新。
5.5.2 常用参数
| 参数 | 说明 |
|---|---|
| label | 标签(浮动提示) |
| placeholder | 占位提示 |
| leadingIcon / trailingIcon | 前置/后置图标 |
| isError | 是否显示错误状态 |
| supportingText | 辅助说明文字(常用于错误提示) |
| keyboardOptions | 键盘类型(数字、邮箱等) |
| keyboardActions | 键盘动作(如「完成」按钮点击) |
| visualTransformation | 视觉变换(如密码遮罩) |
5.5.3 实战:带验证的输入框
var email by remember { mutableStateOf("") }
var isError by remember { mutableStateOf(false) }
OutlinedTextField(
value = email,
onValueChange = {
email = it
isError = !it.contains("@")
},
label = { Text("邮箱") },
isError = isError,
supportingText = {
if (isError) {
Text("请输入有效的邮箱地址", color = MaterialTheme.colorScheme.error)
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
visualTransformation = PasswordVisualTransformation(),否则密码会明文显示。另外,singleLine = true 可以限制输入框为单行,避免用户输入大量文本导致布局异常。
5.6 知识体系总览
下面这张图概括了本章的核心内容,我把它画成了 SVG,方便你对照复习:
文本处理看似简单,但细节很多。从基础的 Text 展示,到 Style 复用,再到 AnnotatedString 富文本和 TextField 交互,每一步都有值得深挖的地方。我个人觉得,掌握好这些内容,你的 Compose UI 功底就已经超过大多数人了。
最后提醒一句:多写、多试。光看文档是不够的,打开 Android Studio,把上面的代码敲一遍,改改参数看看效果。实践出真知,这话在编程领域永远不过时。