7、按钮与交互:Button、IconButton、TextButton、OutlinedButton 的使用、点击事件处理
按钮,是任何 UI 系统里最基础、也最关键的交互元素。在 Compose 里,Google 提供了几种开箱即用的按钮组件:Button、IconButton、TextButton、OutlinedButton。说实话,刚开始接触时我也觉得「不就一个按钮嘛,能有多大区别?」—— 但实际项目做多了,你会发现每种按钮都有自己的「脾气」和适用场景。
核心要点:Compose 的按钮本质上是 Surface + Clickable 的封装。理解这一点,你就能灵活定制任何样式的按钮。
7.1 Button:最常用的填充按钮
Button 是默认的「主角按钮」。它带有背景色填充,视觉权重最高。我个人习惯在「提交」、「保存」、「确认」这类主要操作上使用它。
@Composable
fun PrimaryButtonExample() {
Button(
onClick = { /* 处理点击事件 */ },
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
enabled = true,
colors = ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
) {
Text("提交订单")
}
}
小技巧:如果你需要按钮在加载时禁用,可以用 enabled = !isLoading。我在电商项目里就是这么做的,防止用户重复提交订单。
7.2 IconButton:图标按钮的妙用
IconButton 只包含一个图标,没有文字。它非常适合放在 TopAppBar、列表项尾部等紧凑空间。你想想看,如果每个操作按钮都带文字,界面得多拥挤?
@Composable
fun IconButtonExample() {
IconButton(
onClick = { /* 收藏/取消收藏 */ },
modifier = Modifier.size(48.dp)
) {
Icon(
imageVector = Icons.Default.FavoriteBorder,
contentDescription = "收藏",
tint = MaterialTheme.colorScheme.primary
)
}
}
注意:contentDescription 一定要填!无障碍测试会检查这个。我曾经因为漏写被 QA 打回,后来养成了习惯,每个 Icon 都配上描述。
7.3 TextButton:低调的文字按钮
TextButton 没有边框,没有背景色,只有文字本身。它适合「取消」、「跳过」、「了解更多」这类次要操作。说白了,就是给用户一个「退路」。
@Composable
fun TextButtonExample() {
TextButton(
onClick = { /* 跳过当前步骤 */ },
modifier = Modifier.padding(8.dp)
) {
Text("跳过", style = MaterialTheme.typography.labelLarge)
}
}
嗯,这里要注意:TextButton 的点击区域其实不小,默认有 48dp 的最小高度。所以别担心用户点不到。
7.4 OutlinedButton:带边框的按钮
OutlinedButton 介于 Button 和 TextButton 之间。它有边框,但没有填充背景。适合「注册」、「登录」这类并列操作——比如登录页里,「登录」用 Button,「注册」用 OutlinedButton。
@Composable
fun OutlinedButtonExample() {
OutlinedButton(
onClick = { /* 跳转到注册页 */ },
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline),
modifier = Modifier.fillMaxWidth()
) {
Text("注册新账号")
}
}
对比总结:
| 按钮类型 | 视觉权重 | 典型场景 |
|---|---|---|
| Button | 高 | 主要操作(提交、保存) |
| IconButton | 中 | 图标操作(收藏、删除) |
| TextButton | 低 | 次要操作(取消、跳过) |
| OutlinedButton | 中 | 并列操作(注册、详情) |
7.5 点击事件处理:不止是 onClick
所有按钮都通过 onClick 参数处理点击。但实际项目中,我们往往需要更精细的控制。比如:防抖、长按、双击。
// 防抖点击:防止重复提交
var lastClickTime by remember { mutableStateOf(0L) }
Button(
onClick = {
val now = System.currentTimeMillis()
if (now - lastClickTime > 1000) { // 1秒内只响应一次
lastClickTime = now
// 执行实际逻辑
}
}
) {
Text("提交")
}
我的经验:在金融类 App 里,防抖是必须的。我曾经遇到用户快速点击两次「转账」,结果发了两次请求。后来我在基类里统一封装了防抖逻辑,所有按钮都继承这个行为。
7.6 自定义按钮样式
Compose 的按钮高度可定制。你可以通过 ButtonDefaults 调整颜色、形状、阴影。甚至可以用 Surface + clickable 完全自定义。
Button(
onClick = { /* 自定义样式按钮 */ },
shape = RoundedCornerShape(12.dp),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 4.dp,
pressedElevation = 8.dp
),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFFF6B35),
contentColor = Color.White
)
) {
Icon(Icons.Default.ShoppingCart, contentDescription = null)
Spacer(modifier = Modifier.width(8.dp))
Text("加入购物车")
}
避坑指南:自定义 elevation 时,记得设置 pressedElevation。我曾经只设了 defaultElevation,结果按下时阴影消失,交互反馈很生硬。
7.7 实战:一个完整的按钮交互示例
最后,我们把这些知识点串起来。下面是一个登录页面的按钮组合:
@Composable
fun LoginButtonGroup(
onLogin: () -> Unit,
onRegister: () -> Unit,
onForgotPassword: () -> Unit,
isLoading: Boolean
) {
Column(
modifier = Modifier.fillMaxWidth().padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// 主要操作:登录
Button(
onClick = onLogin,
enabled = !isLoading,
modifier = Modifier.fillMaxWidth()
) {
if (isLoading) {
CircularProgressIndicator(
modifier = Modifier.size(20.dp),
strokeWidth = 2.dp,
color = MaterialTheme.colorScheme.onPrimary
)
Spacer(modifier = Modifier.width(8.dp))
}
Text(if (isLoading) "登录中..." else "登录")
}
// 次要操作:注册
OutlinedButton(
onClick = onRegister,
modifier = Modifier.fillMaxWidth()
) {
Text("注册新账号")
}
// 低强调操作:忘记密码
TextButton(
onClick = onForgotPassword,
modifier = Modifier.align(Alignment.CenterHorizontally)
) {
Text("忘记密码?")
}
}
}
你看,三种按钮放在一起,视觉层次非常清晰。用户一眼就能看出「登录」是主要操作,「注册」是次要选择,「忘记密码」是辅助功能。这就是 Material Design 的按钮设计哲学。
核心总结:
Button:填充背景,高强调,用于主要操作IconButton:纯图标,紧凑,用于工具栏或列表TextButton:无边框,低强调,用于次要或辅助操作OutlinedButton:有边框,中等强调,用于并列操作- 所有按钮通过
onClick处理点击,注意防抖和加载状态 - 通过
ButtonDefaults和shape可以高度自定义样式
嗯,按钮这部分其实不难,但细节很多。我建议你在项目里多试试不同组合,找到最适合自己产品的交互节奏。