D3a基础组件
天天用的积木:Text、按钮、输入、图、开关、进度、分隔。每个组件固定:概念 → XML → ★/○ → 易错 → ▶案例(含示意)。
🔗 引用:Compose 组件
Text ★
概念
显示一段文字。样式优先走 MaterialTheme.typography,少写死字号,全 App 才统一。
和 XML 对照
≈ TextView。XML 常靠 textAppearance / style;Compose 用 style / color / maxLines 等参数。
怎么用(★)
@Composable
fun TextBasics() {
Text(
text = "标题文字",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
○ 偶尔:AnnotatedString 局部加粗/链接;自定义 fontFamily。
易错点
- 到处
fontSize = 18.sp→ 主题一改全盘不一致。 - 长文不设
maxLines+overflow→ 把布局撑破。
▶ 标题 + 辅助说明两行样式
@Composable
fun TitleWithCaption() {
Column {
Text(
"账号安全",
style = MaterialTheme.typography.titleMedium
)
Text(
"管理登录方式与验证设备",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
💡 贴进 Studio 加 @Preview 看字阶。@Preview
Button / TextButton / OutlinedButton ★
概念
三种常见强调级别:实心主操作、描边次要、纯文字弱操作。都是 onClick + 内容 lambda(通常再放 Text / Icon)。
和 XML 对照
≈ Button / Material MaterialButton 的 filled / outlined / text 样式。Compose 用不同函数名表达风格。
怎么用(★)
@Composable
fun ButtonBasics(onPrimary: () -> Unit) {
Button(onClick = onPrimary, enabled = true) {
Text("保存")
}
OutlinedButton(onClick = { }) {
Text("取消")
}
TextButton(onClick = { }) {
Text("了解更多")
}
}
○:IconButton、FloatingActionButton(结构章 / 总表再展开)。
易错点
- 把文案写在
Button参数里而不是内容 lambda —— API 是Button { Text(...) }。 - 主次不分:一屏多个实心 Button,视觉抢戏。
▶ 主按钮 + 次要文字按钮
@Composable
fun LoginActions(
onLogin: () -> Unit,
onForgot: () -> Unit
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Button(onClick = onLogin) {
Text("登录")
}
Spacer(modifier = Modifier.width(12.dp))
TextButton(onClick = onForgot) {
Text("忘记密码?")
}
}
}
OutlinedTextField / TextField ★
概念
受控输入:value + onValueChange 必须接状态。日常更常用描边款 OutlinedTextField。
和 XML 对照
≈ EditText / TextInputLayout。XML 用监听取文案;Compose 状态即文案。
怎么用(★)
@Composable
fun FieldBasics() {
var name by remember { mutableStateOf("") }
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("用户名") },
placeholder = { Text("请输入") },
singleLine = true,
isError = false,
supportingText = null,
modifier = Modifier.fillMaxWidth()
)
}
○:visualTransformation(密码遮罩)、自定义 keyboardOptions。
易错点
- 只写
value不写onValueChange→ 打不了字。 - 状态没
remember→ 重组被清空(见状态章)。
▶ 账号输入:受控 value + 错误提示
@Composable
fun AccountField() {
var account by remember { mutableStateOf("") }
val error = account.isNotEmpty() && account.length < 3
OutlinedTextField(
value = account,
onValueChange = { account = it },
label = { Text("账号") },
isError = error,
supportingText = {
if (error) Text("至少 3 个字符")
},
singleLine = true,
modifier = Modifier.fillMaxWidth()
)
}
要点:错误与否也是状态的函数,和登录按钮 enabled 同一套路。
Image ★ · AsyncImage(Coil)★
概念
本地资源用 Image(painterResource);网络图用 Coil 的 AsyncImage(异步 + 缓存)。装饰图 contentDescription = null;有含义的图要写描述(无障碍)。
和 XML 对照
≈ ImageView + Glide/Coil。Compose 本地走 painter;网络仍推荐 Coil。
怎么用(★)
@Composable
fun LocalAvatar() {
Image(
painter = painterResource(R.drawable.ic_avatar),
contentDescription = "头像",
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
)
}
@Composable
fun NetworkAvatar(url: String) {
AsyncImage(
model = url,
contentDescription = "头像",
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
)
}
○:ContentScale、placeholder / error 画家(Coil 参数)。
易错点
- 网络图用同步解码堵主线程 —— 用 AsyncImage / rememberAsyncImagePainter。
- 有意义的图标
contentDescription = null→ 读屏读不到。
▶ 头像本地图 vs 网络图
依赖:Coil 的 coil-compose。本地放 res/drawable;网络传 URL 字符串即可。
🔗 加载图片
Switch / Checkbox / RadioButton ★
概念
布尔或互斥选择:开关、勾选、单选。一律 checked + onCheckedChange(或 Radio 的 selected 模式)。
和 XML 对照
≈ Switch / CheckBox / RadioButton。XML 监听改 UI;Compose 改状态即可。
怎么用(★)
@Composable
fun ToggleBasics() {
var on by remember { mutableStateOf(true) }
Switch(checked = on, onCheckedChange = { on = it })
var checked by remember { mutableStateOf(false) }
Checkbox(checked = checked, onCheckedChange = { checked = it })
}
易错点
- 只改
checked字面量、没有状态 → 点了不变。 - 整行要可点时,把状态提升到 Row 的
clickable(见案例)。
▶ 设置里的开关行
@Composable
fun NotificationSwitchRow() {
var enabled by remember { mutableStateOf(true) }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { enabled = !enabled }
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text("消息通知", modifier = Modifier.weight(1f))
Switch(
checked = enabled,
onCheckedChange = { enabled = it }
)
}
}
CircularProgressIndicator / LinearProgressIndicator ★
概念
加载反馈:转圈或横条。不确定进度用默认动画;确定进度传 progress。
和 XML 对照
≈ ProgressBar(circular / horizontal)。
怎么用(★)
@Composable
fun ProgressBasics(loading: Boolean) {
if (loading) {
CircularProgressIndicator()
}
LinearProgressIndicator(
progress = { 0.4f },
modifier = Modifier.fillMaxWidth()
)
}
易错点
- 加载结束不把状态关掉 → 转圈永久转。
- 阻塞主线程却指望 Indicator「表示在忙」——指示器只是 UI,耗时仍要丢后台。
▶ 提交中按钮旁小转圈
@Composable
fun SubmitRow(
submitting: Boolean,
onSubmit: () -> Unit
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Button(
onClick = onSubmit,
enabled = !submitting
) {
Text(if (submitting) "提交中…" else "提交")
}
if (submitting) {
Spacer(modifier = Modifier.width(12.dp))
CircularProgressIndicator(
modifier = Modifier.size(24.dp),
strokeWidth = 2.dp
)
}
}
}
Spacer / Divider ★
概念
Spacer 占位空白;HorizontalDivider(或旧名 Divider)画分隔线。比硬写空 Text(" ") 干净。
和 XML 对照
Spacer ≈ 固定宽高的空 View / margin;Divider ≈ View 1dp + background。
怎么用(★)
@Composable
fun SpaceBasics() {
Column {
Text("上一段")
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(16.dp))
Text("下一段")
}
}
易错点
- 能用
Arrangement.spacedBy就少堆 Spacer。 - 分割线忘了水平
fillMaxWidth()(视 Material 版本 API 而定)。
▶ 表单分段
@Composable
fun FormSections() {
Column(modifier = Modifier.padding(16.dp)) {
Text("基本信息", style = MaterialTheme.typography.titleSmall)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(value = "", onValueChange = { }, label = { Text("昵称") })
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(16.dp))
Text("安全设置", style = MaterialTheme.typography.titleSmall)
}
}
○ Slider · Chip · DropdownMenu
概念
用得到再查:滑动条、芯片标签、下拉菜单。主线先混个眼熟,细节回组件总表 / 官网。
和 XML 对照
Slider ≈ SeekBar;Chip ≈ Material Chip;Dropdown ≈ PopupMenu / Spinner。
怎么用(点到)
@Composable
fun OccasionalBasics() {
var v by remember { mutableStateOf(0.3f) }
Slider(value = v, onValueChange = { v = it })
AssistChip(
onClick = { },
label = { Text("标签") }
)
}
易错点
Dropdown 要自己管 expanded 状态;别和「偶尔才用」的复杂度纠缠进主线。
▶ 点到即可
需要完整菜单、筛选芯片组时,打开总表或官网组件页按需抄。