material-dialogs 3.3.0 发布详解:消息行距主题属性、选择列表防护与底部弹窗修复
UI组件移动开发【免费下载链接】material-dialogs A beautiful, fluid, and extensible dialogs API for Kotlin Android.项目地址https://gitcode.com/gh_mirrors/ma/material-dialogs点击查看免费下载导读本文基于 RELEASE_NOTES.md 中的 3.3.0 版本发布说明逐条拆解该版本引入的 6 项功能变更与缺陷修复新增全局消息行距主题属性、为选择列表适配器补充越界断言、修正底部弹窗圆角绘制、修复自定义字体下的标题截断、调整自定义视图垂直内边距逻辑以及让输入对话框遵循全局textInputStyle。每一条变更都结合本仓库 core / bottomsheets / input 模块的源码实现展开读者可据此理解这些改动背后的设计意图并为自己的主题定制、列表数据变更和自定义视图布局实践提供可直接借鉴的源码级方案。版本总览3.3.0 改了什么3.3.0 是一个以「稳定性与主题一致化」为主基调的小版本共包含 6 项变更新增md_line_spacing_body全局主题属性用于统一设置消息行距对应 issue #1903为选择列表适配器增加断言与健全性检查避免越界导致崩溃#1906圆角不再应用到底部弹窗的底部边缘#1941修复自定义字体下对话框标题被截断的问题#1936当customView(...)设置了noVerticalPadding且scrollable开启时内容ScrollView底部不再被追加内边距#1834输入对话框不再由对话框自身强制样式而是改用全局TextInputLayout的textInputStyle默认值#1857。下面逐一结合源码深入说明。新增全局主题属性md_line_spacing_body#1903属性定义与默认值md_line_spacing_body是一个float类型的主题属性定义在 core/src/main/res-public/values/public.xmlattr formatfloat namemd_line_spacing_body/它被放置在res-public中意味着该属性属于库对外暴露的公共 API主题定制者可以在自己的 App 主题中覆盖它。在消息设置中的消费逻辑属性消费逻辑位于 DialogMessageSettings.kt 的setText方法中internal fun setText( StringRes res: Int?, text: CharSequence? ) { if (!didSetLineSpacing) { lineSpacing( resolveFloat( context dialog.windowContext, attr R.attr.md_line_spacing_body, defaultValue 1.1f ) ) } messageTextView.text text.maybeWrapHtml(isHtml) ?: resolveString(dialog, res, html isHtml) }这里有两个关键设计点默认值 1.1f当主题中没有显式配置md_line_spacing_body时消息行距取 1.1setLineSpacing(0f, multiplier)的 multiplier 语义。调用方优先级didSetLineSpacing标志保证了如果调用方通过 lineSpacing(multiplier: Float) 显式设置过行距则全局主题默认值不会覆盖显式设置fun lineSpacing(multiplier: Float): DialogMessageSettings { didSetLineSpacing true messageTextView.setLineSpacing(0f, multiplier) return this }实际使用方式在 App 主题中声明该属性即可让所有对话框消息统一行距无需逐次调用lineSpacing(...)style nameAppTheme parentTheme.MaterialComponents.DayNight item namemd_line_spacing_body1.4/item /style若某个对话框需要特殊行距仍可通过message { lineSpacing(1.6f) }按需覆盖。该改动把「行距」从逐对话框样板代码提升为全局主题能力是从「编程式配置」走向「主题式配置」的典型模式。选择列表适配器的越界断言与健全性检查#1906问题背景当开发者在列表显示后调用checkItems(...)/uncheckItems(...)等方法且传入的索引超出当前items集合范围时旧版本可能抛出难以定位的数组越界或元素找不到异常。3.3.0 在适配器层加入了显式断言将错误提前暴露并给出可读信息。多选适配器的实现在 MultiChoiceDialogAdapter.kt 中checkItems与uncheckItems都加入了同样的范围校验override fun checkItems(indices: IntArray) { val existingSelection this.currentSelection val indicesToAdd indices.filter { check(it 0 it items.size) { Index $it is out of range for this adapter of ${items.size} items. } !existingSelection.contains(it) } this.currentSelection this.currentSelection.appendAll(indicesToAdd) ... } override fun uncheckItems(indices: IntArray) { val existingSelection this.currentSelection val indicesToAdd indices.filter { check(it 0 it items.size) { Index $it is out of range for this adapter of ${items.size} items. } existingSelection.contains(it) } ... }采用checkIllegalStateException而非静默忽略是因为越界索引几乎总是调用方 bug——提前在失败现场抛出带上下文期望索引 vs 实际条目数的异常比后续在notifyItemChanged处崩溃更容易排查。单选适配器的实现SingleChoiceDialogAdapter.kt 中的checkItems/uncheckItems同样增加了断言override fun checkItems(indices: IntArray) { val targetIndex if (indices.isNotEmpty()) indices[0] else -1 check(targetIndex 0 targetIndex items.size) { Index $targetIndex is out of range for this adapter of ${items.size} items. } if (this.disabledIndices.contains(targetIndex)) return this.currentSelection targetIndex }点击事件侧的防护除 API 入口外两个适配器的 ViewHolder 点击回调也都加入了if (adapterPosition 0) return防护见 MultiChoiceDialogAdapter.kt 与 SingleChoiceDialogAdapter.kt避免 RecyclerView 复用过程中adapterPosition为NO_POSITION-1时触发越界访问override fun onClick(view: View) { if (adapterPosition 0) return adapter.itemClicked(adapterPosition) }对使用replaceItems(...)动态更新条目、随后再以旧索引操作选中的场景这一组检查能显著降低运行时崩溃概率。底部弹窗圆角不再应用到底部边缘#1941背景Material 底部弹窗Bottom Sheet本质上是「贴底浮出」的面板其视觉语义要求底部边缘与屏幕底部对齐不应出现圆角此前版本在部分场景下底部两角仍会绘制圆角与预期不符。修复后的圆角绘制BottomSheet行为类在 BottomSheet.kt 的setBackgroundColor中通过GradientDrawable.cornerRadii显式将底部两角置为 0override fun setBackgroundColor( view: DialogLayout, color: Int, cornerRadius: Float ) { bottomSheetView.background GradientDrawable().apply { this.cornerRadii floatArrayOf( cornerRadius, cornerRadius, // top left cornerRadius, cornerRadius, // top right 0f, 0f, // bottom left 0f, 0f // bottom right ) setColor(color) } buttonsLayout.setBackgroundColor(color) }与模态对话框的对比对比常规模态对话框ModalDialog的同一方法DialogBehavior.kt其cornerRadii同样是「顶部两角圆角、底部两角 0」但原因不同——模态对话框通常不会将背景一直延伸到屏幕底部四角本就走顶部圆角方案。两处的cornerRadii最终都会传递到根布局 DialogLayout在onMeasure中构建addRoundRect路径并在dispatchDraw中通过canvas.clipPath(cornerRadiusPath)裁剪绘制。这一改动确保底部弹窗无论配置多大的md_corner_radius或调用 cornerRadius(...)贴底的一侧都保持直角符合 Material Design 对底部面板的视觉规范。修复自定义字体下标题被截断#1936问题根因当通过md_font_title解析逻辑见 MaterialDialog.kt配置非常规字体时部分字体的fontMetrics上下跨度ascent/descent会大于系统默认字体导致按默认字体计算的标题高度在底部被截断。修复实现动态计算额外内边距MDUtil.kt 中新增了additionalPaddingForFont()辅助方法其源码注释直接引用了该 issue 编号/** * See [https://github.com/afollestad/material-dialogs/issues/1936]. Calculates additional * spacing required to prevent a given [TextView] from being cut off at the bottom. */ RestrictTo(LIBRARY_GROUP) fun TextView.additionalPaddingForFont(): Int { val fm paint.fontMetrics val textHeight fm.descent - fm.ascent return if (textHeight measuredHeight) (textHeight - measuredHeight).toInt() else 0 }原理用paint.fontMetrics计算当前字体实际需要的文本高度descent - ascent若大于 TextView 的measuredHeight则返回差值作为额外空间。在标题布局中的接入DialogTitleLayout.kt 的onLayout中标题底部位置加入了这一补偿值val titleTop contentMidPoint - titleHalfHeight val titleBottom contentMidPoint titleHalfHeight titleView.additionalPaddingForFont()由于该补偿仅在字体实际需求超出测量高度时生效否则返回 0对默认字体场景零开销、零影响实现了「按需补偿」的最小侵入式修复。customView 的 noVerticalPadding 与滚动容器内边距#1834参数语义customView(...) 提供noVerticalPadding参数官方 KDoc 描述为「为 true 时内容四周不添加垂直内边距」。该参数同时被写入配置键CUSTOM_VIEW_NO_VERTICAL_PADDING见 DialogCustomViewExt.ktfun MaterialDialog.customView( LayoutRes viewRes: Int? null, view: View? null, scrollable: Boolean false, noVerticalPadding: Boolean false, horizontalPadding: Boolean false, dialogWrapContent: Boolean false ): MaterialDialog { assertOneSet(customView, view, viewRes) config[CUSTOM_VIEW_NO_VERTICAL_PADDING] noVerticalPadding ... }修复前的问题当scrollable true时自定义视图会被放进对话框的ScrollViewDialogContentLayout.kt。旧逻辑中即使设置了noVerticalPaddingScrollView底部仍会保留md_dialog_frame_margin_vertical的内边距导致「无垂直内边距」的承诺无法兑现。修复后的行为addContentScrollView在创建滚动容器时读取该开关DialogContentLayout.ktprivate fun addContentScrollView( noVerticalPadding: Boolean ) { if (scrollView null) { scrollView inflateDialogScrollView(R.layout.md_dialog_stub_scrollview).apply { this.rootView rootLayout scrollFrame this.getChildAt(0) as ViewGroup if (!noVerticalPadding) { val scrollBottomPadding dimenPx(R.dimen.md_dialog_frame_margin_vertical) updatePadding(bottom scrollBottomPadding) } } addView(scrollView) } }即只有noVerticalPadding false时才给滚动容器追加底部内边距为 true 时完全跳过。对于「自定义视图自带底部留白」或「需要贴边滚动到底」的布局现在可以真正实现无缝衔接。输入对话框样式改由全局 textInputStyle 控制#1857变更意图此前输入对话框会由库强制应用自己的输入样式导致开发者在 App 全局配置的TextInputLayout样式textInputStyle无法生效。3.3.0 之后库不再强制覆盖输入框外观完全遵循宿主 App 的主题默认值保持与 App 其余输入框一致。源码佐证input() 通过customView(R.layout.md_dialog_stub_input)将 md_dialog_stub_input.xml 作为自定义视图载入其内部包含TextInputLayout。随后styleInputDialogInputExt.kt只负责设置 hint、输入类型、文本/提示文字颜色与 bodyFont不再触碰输入框的容器样式private fun MaterialDialog.styleInput( hint: String?, hintRes: Int?, inputType: Int ) { val resources windowContext.resources val editText getInputField() getInputLayout().hint hint ?: if (hintRes ! null) resources.getString(hintRes) else null editText.inputType inputType editText.maybeSetTextColor( windowContext, attrRes R.attr.md_color_content, hintAttrRes R.attr.md_color_hint ) bodyFont?.let(editText::setTypeface) }对使用者的影响若你的 App 已在主题中配置textInputStyle如自定义填充色、圆角、字体升级到 3.3.0 后对话框输入框会自动跟随无需额外代码。若需要为某个输入对话框单独定制仍可通过 getInputLayout() 拿到TextInputLayout实例在其上继续做局部定制。同时input(...)保留的hint/hintRes/prefill/maxLength/waitForPositiveButton/allowEmpty等参数语义均未改变DialogInputExt.ktmaxLength仍通过isCounterEnabled与counterMaxLength启用字数计数器。小结与升级建议3.3.0 的六项改动可以归纳为三条主线主题化能力增强md_line_spacing_body让消息行距进入全局主题体系与既有的md_font_body、md_color_content等属性形成一致的主题矩阵输入框则让位于 App 全局textInputStyle进一步减少库对视觉的强占。健壮性提升选择列表适配器的显式越界断言与 ViewHolder 的adapterPosition防护把「静默异常」变为「带上下文的即时异常」尤其利好动态replaceItems 程序化选中的场景。布局细节修正底部弹窗底部圆角归零、自定义字体标题动态补偿高度、noVerticalPadding对滚动容器真正生效三处都属于对视觉与测量细节的打磨。对于已经使用 material-dialogs 的项目建议升级后在以下位置做一次回归验证带自定义md_font_title的对话框标题显示、scrollable noVerticalPadding的自定义视图底部间距、底部弹窗在自定义md_corner_radius下的四角表现以及 App 全局textInputStyle在输入对话框中的生效情况。相关实现细节均可直接阅读 core、bottomsheets 与 input 模块的对应源码文件。赞分享UI组件移动开发【免费下载链接】material-dialogs A beautiful, fluid, and extensible dialogs API for Kotlin Android.项目地址https://gitcode.com/gh_mirrors/ma/material-dialogs点击查看免费下载相关推荐告别繁琐弹窗开发Material Dialogs Bottom Sheets 快速实现优雅底部交互告别繁琐弹窗开发Material Dialogs Bottom Sheets 快速实现优雅底部交互 还在为Android底部弹窗适配头疼Material DUI组件移动开发攻克Material Dialogs布局难题内边距与外边距全解析攻克Material Dialogs布局难题内边距与外边距全解析 你是否还在为对话框布局错位而头疼是否因边距设置不当导致UI在不同设备上显示异常本文将带你UI组件移动开发BottomSheet 项目推荐Android 底部弹窗组件的革命性选择BottomSheet 项目推荐Android 底部弹窗组件的革命性选择 痛点传统对话框的局限性 在 Android 应用开发中对话框Dialog和菜UI组件上一篇Buzz 上手指南本地离线转录免费把录音转成文字和字幕下一篇离线音频转录完全指南Buzz如何在本地保护你的数据创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Vue Router 2 的 `<router-link>` 组件完全指南:导航、属性与激活状态原理

Vue Router 2 的 `<router-link>` 组件完全指南:导航、属性与激活状态原理

前端路由 【免费下载链接】vue-router &#x1f6a6; The official router for Vue 2 项目地址&#xff1a; https://gitcode.com/gh_mirrors/vu/vue-router 点击查看 免费下载 <router-link> 是 Vue Router&#xff08;Vue 2&#xff09;中用于驱动用户导航的核心内置组…

2026/9/20 19:58:44 阅读更多 →
PTO-ISA TPOP 指令详解:从 TPipe FIFO 弹出消费者 Tile 的 Cube-Vector 数据通路编程

PTO-ISA TPOP 指令详解:从 TPipe FIFO 弹出消费者 Tile 的 Cube-Vector 数据通路编程

PTO-ISA TPOP 指令详解&#xff1a;从 TPipe FIFO 弹出消费者 Tile 的 Cube-Vector 数据通路编程 【免费下载链接】pto-isa Parallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repo…

2026/9/20 19:58:44 阅读更多 →
Cherry Studio 应用版本(Edition)与 Provider 可用性跟随机制:全局版/中国版数据共享下的模型可见性实践

Cherry Studio 应用版本(Edition)与 Provider 可用性跟随机制:全局版/中国版数据共享下的模型可见性实践

人工智能大模型AI 应用交互助手本地部署 【免费下载链接】cherry-studio &#x1f352; Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端 项目地址&#xff1a; https://gitcode.com/CherryHQ/cherry-studio 点击查看 免费下载 导读 Cherry Studio 提供全局版&#xff…

2026/9/20 19:58:44 阅读更多 →

最新新闻

三步跑通 GetQzonehistory:把 QQ 空间历史说说完整导出的指南

三步跑通 GetQzonehistory:把 QQ 空间历史说说完整导出的指南

三步跑通 GetQzonehistory&#xff1a;把 QQ 空间历史说说完整导出的指南 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory GetQzonehistory 是一个 Python 工具&#xff0c;帮你登录自己…

2026/9/20 21:31:41 阅读更多 →
2026年AIGC论文降重工具评测与使用指南

2026年AIGC论文降重工具评测与使用指南

1. 毕业论文降重工具现状解析2026年的学术环境中&#xff0c;论文降重已成为毕业生必经的考验。随着检测算法不断升级&#xff0c;传统"同义词替换语序调整"的降重方式已难以应对查重系统的语义分析能力。目前主流降重工具可分为三类&#xff1a;基础改写类&#xff…

2026/9/20 21:31:41 阅读更多 →
2026年企业AI趋势:异构计算与边缘智能的崛起

2026年企业AI趋势:异构计算与边缘智能的崛起

1. 企业AI趋势报告的价值与背景2026年企业AI十大趋势预测报告的发布&#xff0c;标志着AI技术在企业应用领域进入了一个新的成熟阶段。这类趋势报告不同于普通的行业分析&#xff0c;它是由具备全球技术视野和丰富落地经验的企业基于大量实际案例和数据提炼而成。作为从业15年的…

2026/9/20 21:31:41 阅读更多 →
站点资源归档:全量索引页生成与日期戳版本管理实操

站点资源归档:全量索引页生成与日期戳版本管理实操

1. 从一个链接说起&#xff1a;这个页面到底在做什么第一次看到http://www.115ps.com/all.html,plnt-20200804这个字符串&#xff0c;很多人的第一反应是“这不就是个网址吗”。但如果你真的在浏览器里敲进去&#xff0c;会发现它并不是一个常规的可访问页面——all.html后面跟…

2026/9/20 21:31:41 阅读更多 →
notepad-- 新手完整指南:编辑、文件对比、批量查找,一台跨平台文本编辑器全包

notepad-- 新手完整指南:编辑、文件对比、批量查找,一台跨平台文本编辑器全包

notepad-- 新手完整指南&#xff1a;编辑、文件对比、批量查找&#xff0c;一台跨平台文本编辑器全包 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器&#xff0c;目标是做中国人自己的编辑器&#xff0c;来自中国。 项目地址: https://gitcode.com/GitHu…

2026/9/20 21:31:41 阅读更多 →
.NET 命令行配置提供器实战指南:深入 Microsoft.Extensions.Configuration.CommandLine 的用法与实现原理

.NET 命令行配置提供器实战指南:深入 Microsoft.Extensions.Configuration.CommandLine 的用法与实现原理

语言运行时标准库JIT编译编译器 【免费下载链接】runtime .NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps. 项目地址&#xff1a; https://gitcode.com/GitHub_Trending/runtime6/runtime 点击查看 免费下载 本文以 dotnet/runtime 仓库中的 Mic…

2026/9/20 21:30:40 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事&#xff1a;用Flutter给OpenHarmony做一款游戏集合类的App&#xff0c;说白了就是把若干小游戏塞进一个壳里&#xff0c;用统一入口分发。这个方向本身不算新鲜&#xff0c;真正让我花了不少心思的&#xff0c;是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档&#xff0c;最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事&#xff1a;今天在表后面多加了两个空白行&#xff0c;明天给客户交稿前发现整个章节的编号全部错位&#xff0c;光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年&#xff0c;说实话&#xff0c;第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年&#xff0c;流量惨淡、功能臃肿、代码自己都懒得看第二遍之后&#xff0c;我才慢慢琢磨明白一个道理&#xff1a;第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事&#xff1a;用Flutter给OpenHarmony做一款游戏集合类的App&#xff0c;说白了就是把若干小游戏塞进一个壳里&#xff0c;用统一入口分发。这个方向本身不算新鲜&#xff0c;真正让我花了不少心思的&#xff0c;是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档&#xff0c;最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事&#xff1a;今天在表后面多加了两个空白行&#xff0c;明天给客户交稿前发现整个章节的编号全部错位&#xff0c;光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年&#xff0c;说实话&#xff0c;第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年&#xff0c;流量惨淡、功能臃肿、代码自己都懒得看第二遍之后&#xff0c;我才慢慢琢磨明白一个道理&#xff1a;第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践&#xff1a;原型怎样变成可用功能分类&#xff1a;[AI/大模型]细分主题&#xff1a;AI 增强型 CI/CD 流水线自动化与 GitOps 实践&#xff1a;Agent 工作流、工具调用与任务拆解&#xff1a;从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战&#xff1a;复盘记录怎样真正派上用场分类&#xff1a;[工程技术]细分主题&#xff1a;Kubernetes 生产环境运维与排障实战&#xff1a;可复制的项目复盘模板与决策记录大部分团队的事故复盘报告&#xff0c;最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理&#xff1a;核心链路应该先拆哪一步分类&#xff1a;[工程技术]细分主题&#xff1a;Docker 容器化技术与镜像安全管理&#xff1a;核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用&#xff08;包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →