当前位置: 首页 > news >正文

Material3 组件选择、状态管理与避坑指南

与官方文档的关系:官方列API 签名与默认样式;本文写「为什么这样配」、易混点、和 View/XML 时代思维差异。请始终以 developer.android.com/jetpack/compose 为准核对版本差异。

配套示例

运行 App →系列示例09|基础控件与 Material3,对照ControlsLabScreen.kt(纵向滚动一屏内包含Text / TextField / Button / Switch / Chip / Radio / Checkbox / Slider / AlertDialog等区块)。

屏内纵向分区(与代码顺序一致)

自上而下与ControlsLabScreen.kt对应关系,方便「一文 + 一屏」对照:

顺序区块要点
1TextmaxLines+TextOverflow.Ellipsis,大字模式/窄屏防撑破
2OutlinedTextField(昵称)value+onValueChange受控;isError+supportingTexttrailingIcon清空
3OutlinedTextField(密码)PasswordVisualTransformation/None切换;KeyboardOptions
4提交Buttonenablednameagreeloading绑定;rememberCoroutineScope().launch { delay }仅演示加载态,生产请ViewModel + 可取消任务
5Switch+ 协议文案与主按钮enabled联动
6AssistChip+FilterChip主行动 vs 筛选语义;FilterChipleadingIcon勾选
7RadioButtonModifier.selectableGroup()+ 行selectable(role = RadioButton)RadioButton(onClick = null)
8Checkbox独立附加选项示例
9Slidersteps = 9valueRange = 0f..1f,展示百分比文案
10AlertDialogshowDialog布尔;onDismissRequest与双按钮一致关窗

受控输入:单一真相(与 01 篇对齐)

value

onValueChange

mutableStateOf / StateFlow

OutlinedTextField

valueonValueChange必须同源;若value来自 ViewModel、onValueChange只写本地remember而不同步上去,会出现光标跳、删字回弹等「输入法与 UI 打架」。


1.Text:别只盯stringResource

技巧

  • 行数与溢出:列表/卡片标题务必显式maxLines+overflow = TextOverflow.Ellipsis,否则在大字模式窄屏下会把父布局撑破或裁切不可预期。
  • style:优先MaterialTheme.typography.*,让字阶随 Theme 变;局部再用merge覆盖fontSize时,注意行高lineHeight是否仍匹配(否则中文易「行间距发飘」)。
  • 富文本链接:新代码优先看Text+LinkAnnotation等 API;旧代码里可能见到AnnotatedString+ClickableText。无论哪种写法,都要把「可点链接」与「整段 onClick」分清,避免命中区域重叠。

避坑

  • Text里拼超长调试字符串 → 重组时分配大量String/CharSequence,热点路径里改用截断日志走 Timber
  • 业务格式化(金额、日期)放在 Composable 每次重组都算:应remember(currency, amount)或下沉ViewModel(与 07 篇derivedStateOf择一或组合)。

2.OutlinedTextField:状态必须「单一真相」

心智value + onValueChange受控组件value来源不清就会输入法与 UI 打架(光标跳、删字回弹)。

技巧

  • isError+supportingText:错误提示别只用Toast;字段旁错误文案更可访问(TalkBack 也能读到)。
  • 密码PasswordVisualTransformation()+ 自管showPassword布尔;若是复杂的自定义VisualTransformation,建议用remember { … }包住,避免重组时反复创建无意义对象。简单内置转换通常问题不大。
  • singleLine = truemaxLines:搜索框常用单行;多行备注用maxLines+ 滚动,别混用语义。

避坑

  • onValueChange里做同步重计算(正则很强、每次全量校验大文本)会卡输入线程感;应防抖或把重校验放LaunchedEffect/ ViewModel(见 03 篇)。
  • readOnly = true仍可获得焦点:若只想展示,考虑Text+Modifier.clickabledisabled语义,别假装是输入框。

3.Button/TextButton/IconButton

技巧

  • 防连点:提交动作应用enabled = !loading或统一在 ViewModel 里串行化提交;本屏用delay+LinearProgressIndicator只演示形态,生产请把异步放进ViewModel并处理取消与重复点击
  • TextButton放在OutlinedTextFieldtrailing:很常见;注意最小触摸目标 48dp(Material 默认会处理一部分,自定义缩得过小会被无障碍打回)。

避坑

  • ButtononClick里直接launch(Dispatchers.IO)且无宿主:泄漏/崩溃难查;用rememberCoroutineScope+ 可取消 JobViewModel

4.Switch/Checkbox/RadioButton

技巧

  • 文案绑定Switch右侧Text与 switch同一语义时,注意读屏是否出现「两个控件」;必要时用toggleable或文案semantics合并。
  • 三态 Checkbox(若仍用 XML 习惯):Compose 默认二态;三态要自定义状态机并在设计稿标明。

避坑

  • AssistChip当「主要提交按钮」:语义不对,点按区域与键盘导航顺序也不友好;主行动用Button,筛选用FilterChip

4.1FilterChipselectedleadingIcon

  • 语义:表示「过滤条件开/关」;selected = true时建议加勾选图标(本仓库用Icons.Default.Check),读屏用户能感知「已应用筛选」。
  • 避坑onClick里同时改多个筛选条件时,注意状态原子性(一次重组内一致),避免半开半关。

4.2RadioButton:别只给圆圈加点按区域

  • 技巧:用Modifier.selectableGroup()包一组,单行Row(Modifier.selectable(role = Role.RadioButton))整行可点RadioButtononClick可置null,由外层selectable统一处理,避免双重点击逻辑。
  • 避坑:三个RadioButton各写独立mutableStateOf<Boolean>易出现多选为真;用单个selectedIndex: Intenum才是单选真相源。

4.3Checkbox:与Switch分工

  • Checkbox:多选列表、条款勾选;Switch:即时生效的设置项。混用会让用户形成错误心理模型。
  • 三态:默认二态;树形「部分选中」要自定义ToggleableState与图标,别硬套默认 Checkbox。

4.4Slidersteps与重组频率

  • steps = 9+valueRange 0f..1f:端点之间有 9 个中间刻度,合计11 个可选值、10 个区间;适合「音量、缩放百分比」等可解释档位。
  • 避坑:拖动时onValueChange极高频;不要把重计算/日志放在 lambda 里。展示字符串可用remember(sliderValue) { … }derivedStateOf(见第 07 篇)。

4.5AlertDialogonDismissRequest是唯一真相吗?

  • 必须决定:蒙层点击、返回键、cancel按钮是否等价于同一业务取消;否则会出现「用户以为取消了,其实后台任务仍在跑」。
  • 避坑:在text = { }里塞大列表 / WebView:对话框不是容器万能胶;复杂内容用全屏路由ModalBottomSheet

5. 图片与图标(本屏未接网络图,只给策略)

  • 图标Icon(imageVector = Icons.Default.xxx, contentDescription = …)contentDescriptionnull仅当装饰性;可点击必须描述。
  • 位图:优先Coil / Glide Compose集成,model+contentScale+约束尺寸;不要在LazyColumnitem 里按原图尺寸 decode

6.Card/Surface/Divider:层次别用错

  • Surface:更底层,tonal elevation、形状、点击波纹常从这里搭;适合作为「一块可交互表面」的根。
  • Card:Material3 的 Card 更偏内容分组;别把整张页面套一层 Card 再套一层 Surface无意义叠高程
  • HorizontalDivider:分隔列表块;在滚动列表里 Divider 过密会显得「碎」;考虑用间距 + 背景对比代替。

7.RangeSlider与连续值(补充)

  • 连续值绑定FloatStatemutableFloatStateOf;拖动中会高频重组,派生文案(如「约等于 12 GB」)用remember(value) { … }
  • 范围选择RangeSlider适合价格区间、时间区间等「起点 + 终点」场景;两端值都要与业务最小粒度对齐,避免用户拖出无法提交的中间值。

8.AlertDialog/ModalBottomSheet(思路)

  • 对话框状态应与导航或单一 boolean明确绑定;dismissRequest里要处理返回键蒙层点击是否等价于取消。
  • BottomSheet与全屏键盘同屏:Insets + sheet 高度一起验收,否则出现「键盘盖住 sheet 按钮」(见 12 篇)。

9. 和「精通段」衔接

  • 下一篇自定义 Layout:当你发现Row/Column表达不了测量策略时,再下沉到Layout
  • 动画篇:控件显隐用AnimatedVisibility比手写alpha更符合可访问性与默认过渡。

10. 自检清单

  1. 受控字段value是否单一来源(本地remember或 ViewModelStateFlow,勿混)?
  2. 提交路径是否在主线程重活 / 无取消?应下沉ViewModel并暴露loading/error(见 01 篇)。
  3. Slider/TextField的高频回调里是否只做O(1) 更新,重逻辑已remember/ 防抖 / 派生状态
  4. Radio / Switch / Checkbox的读屏语义是否与视觉一致(整行可点、单选真相源)?
  5. AlertDialog关闭路径是否与业务取消对齐,无「窗关了任务还在跑」?

参考答案(复习用)

  1. OutlinedTextField(value = x, onValueChange = { x = it })x只来自一处;若 VM 下发state.nameonValueChange必须onEvent写回 VM,不能只在本地改remember
  2. 否才算合格(对生产而言)。本仓库ControlsLabScreenscope.launch { delay }仅演示;生产应在 ViewModel 用viewModelScope+MutableStateFlow/UiState暴露loading/error,并可在离开屏时取消。
  3. 应如此onValueChange只赋值;校验用LaunchedEffect(name)防抖或 VM 内;Slider的展示文案用remember(sliderValue)derivedStateOf,避免在onValueChange里打日志或重算大对象。
  4. 应对齐。单选用一个selectedIndex+selectableGroup+ 行selectableRadioButton(onClick = null);Switch 与文案不要两个独立焦点抢同一业务布尔。
  5. 应对齐onDismissRequest、取消按钮、确认按钮各自应调用同一套「关窗 + 是否取消请求」逻辑;若仅showDialog = false而后台仍launch,属于未对齐。

源码仓库:ComposeDemo(分支main

相关推荐

《重组与参数稳定性:跳过规则、derivedStateOf 与调试》

《测试分层:JVM 单测、ViewModel 测试与 Compose UI Test》

http://www.jsqmd.com/news/866718/

相关文章:

  • 基于OpenHarmony与SC-3568HA的工业网关开发实战:从硬件选型到分布式应用
  • 工业视觉系统精度保障:CCD相机与镜头参数计算实战指南
  • 2026年最新英语作文批改工具推荐:适合学生用的好用清单
  • 构建之法阅读笔记08
  • 基于EsDA平台的串口设备联网与MQTT上云实战指南
  • Prompt工程进阶:从写Prompt到工程化Prompt管理
  • 新能源汽车动力域系统级测试:从HIL到自动化实战指南
  • BetterNCM Installer深度解析:网易云音乐插件管理的完整解决方案
  • 机器学习核心术语手册:从数据到部署的完整概念解析与实战指南
  • 如何将OpenClaw这类Agent工具接入Taotoken多模型服务
  • 当你的线程“互相等待”时:死锁的四个必要条件与 Java 代码中的“致命拥抱”
  • PET_RK3588_P01开发板深度评测:从硬件解析到AI实战应用
  • JTAG操作实战指南:从原理到嵌入式调试与Flash编程
  • 嵌入式AI实战:从模型量化到人形检测部署全流程解析
  • 蛋白质-配体相互作用分析终极指南:PLIP快速入门与实战应用
  • 2026最新北京本地国画艺考画室综合能力测评结果:央美国画培训与中国画校考集训怎么选 - 企业信息深度横评
  • Windows 10 21H1启用包机制解析与部署实战指南
  • SQL学习指南——再谈连接
  • Linux内核调度器心跳机制:scheduler_tick原理与性能调优
  • 新能源动力域系统级测试:从HIL仿真到自动化验证的完整解决方案
  • 基于EsDA平台实现串口设备联网:Modbus RTU转MQTT网关实战
  • Display Driver Uninstaller:彻底解决显卡驱动问题的3步终极指南
  • RISC-V嵌入式AI部署实战:NanoDet模型与ncnn框架移植指南
  • LangGraph实战:构建可控、可调试的复杂AI工作流
  • 抖音下载器:如何永久保存你喜欢的短视频内容?
  • 开源项目功能扩展技术方案:实现多账户管理与配置优化的完整指南
  • 抖音无水印下载终极指南:douyin-downloader让内容保存变得如此简单
  • 深入Linux调度器心跳:scheduler_tick原理、性能影响与调优实践
  • 网盘直链下载助手实战指南:八大平台免登录高速下载完整方案
  • 基于Linux内核list.h思想实现高效C语言单向链表