摘要:对于开发同学来说不管用户比例多高设计稿都按给绝对是最值得吐槽的事情之一在我刚开始接触开发的那个阶段每当有人问起这件事我都说的做法就是看着差不多就行了后来有些要求特别高的设计开发同学就只能很苦逼的一个一个的改到满意为止我看现在不少辅助开发工具
对于 Android 开发同学来说, "不管用户比例多高, 设计稿都按 iOS 给"绝对是最值得吐槽的事情之一.
在我刚开始接触 Android 开发的那个阶段, 每当有人问起这件事, 我都说 "Android 的做法就是看着差不多就行了..." 后来有些要求特别高的设计, Android 开发同学就只能很苦逼的一个 dp 一个 dp 的改到 UI 满意为止. 我看现在不少辅助开发工具的思路也是这样.
17 年底 ~ 18 年初搞 UI 大改版的时候, iOS 开发同学 传人 Joe 跟设计敲定了 iOS 的还原方式. 我觉得如果 Android 不搞的话之后开发就太烦了, 就决定 试一试. 最后的方案虽然不是特别通用, 但也能解决大部分问题.
然后就被设计小姐姐催着写原理, 然后一年就过去了...
从这里开始
上图中, 粉底的 "22 一行"和 "22 多行"是设计小姐姐给的参照图, 是用 Sketch 输出的, 使用 22 号字+默认行高情况下的设计稿上的样子. 绿底和黄底的 "22 一行"是 Nexus 5 上, 使用 22dp 的样子; 蓝底(叠加显示成紫色)的 "22 四行"也是 22dp, 文字用 " " 换行. 底色之间的差异就是"行高"的差异.
可见绿底一行的行高有一点细微的偏差, 黄底一行因为叠加了 4 次这个误差, 比较明显. 多行情况下的误差更大, 因为 Android 和 iOS 在多行文本排版的概念上差异很大. 参照图上单行和多行是能对上的, 现在我们要想办法让 Android 的单行和多行都能跟参照图对上.
单行对齐
观察发现单行差的是底部的一段空白, 我称之为 additionalPaddingBottom. 对比各种字号的情况, 发现并没有规律, 因此搞出来一组经验值.
这个经验值在 3 倍屏上还是比较准确的(最重要的是设计走查就用 3 倍屏...), 单行文字位置和行高都能对上. 在其他倍数的屏幕上基本 ok, 但也有一些异常. 比如在 1.5 倍屏上, 部分字号只能达成行高对的上但文字位置对不上的效果, 而且还受到 setSingleLine 的影响, 15dp + setSingleLine(true) 时偏差尤其大.
/** * density 为 3 时的经验值, 作为计算 additionalLineSpace 的基数 */ static { paddingBottomMap.put(10, 1f / 3); paddingBottomMap.put(11, 4f / 3); paddingBottomMap.put(12, 2f / 3); paddingBottomMap.put(13, 1f / 3); paddingBottomMap.put(14, 3f / 3); paddingBottomMap.put(15, 2f / 3); paddingBottomMap.put(16, 1f / 3); paddingBottomMap.put(17, 4f / 3); paddingBottomMap.put(19, 1f / 3); paddingBottomMap.put(22, 2f / 3); paddingBottomMap.put(30, 5f / 3); }
多行对齐
根据 Android 文本排版概念, 我写了个简单的 MetricsTextView 来确定单行和多行的行高关系:
观察发现: 两行文字的高度 = 单行文字的高度 + 单行文字设置 setIncludeFontPadding(false) 的高度
同时, 两行文字和两组单行的差别在于文字之间的空白, 因此需要增加 lineSpaceExtra = topSpace + bottomSpace + additionalPaddingBottom. 这样 Android 也实现了 n 行文字行高 = n x 单行文字行高, 多行也就对上了.
行高
上面都是参考图使用默认行高的情况, 如果行高变了呢);
/** * sketch 中字号对应的默认行高 (dp) */ static { defaultLineHeightMap.put(10, 14); defaultLineHeightMap.put(11, 16); defaultLineHeightMap.put(12, 17); defaultLineHeightMap.put(13, 18); defaultLineHeightMap.put(14, 20); defaultLineHeightMap.put(15, 21); defaultLineHeightMap.put(16, 22); defaultLineHeightMap.put(17, 24); defaultLineHeightMap.put(19, 26); defaultLineHeightMap.put(30, 42); }
首先我们有默认行高的值, 然后把 deltaPaddingTop = deltaPaddingBottom = (lineHeight - defaultLineHeight) / 2 用 paddingTop 和 paddingBottom 加到 每一行 上 - 实验结果表明上下加的一样多, 可以除 2, 真是幸运.
带行高的对齐:
局限性
只关注行高, 不关注文本宽度, 所以换行还是跟 iOS 不一样.
并不是对所有的字号/字体都有效, 只处理了我们常用的字号(其它字号要加也不难), 默认字体.
没有抽成库, 原因就是上面那条.
18 年 Android 最新的 support 库好像为 AppCompatTextView 增加了行高支持, 但我还没有来得及试.
其它已知问题
5.0 以下系统需要特殊处理 paddingBottom, 因为会增加额外的 lineSpaceExtra
但是有些 5.0 及以上的手机 (vivo X9, 锤子) 居然也有这个问题, 管不了了...
因为直接修改了 TextView 的 paddingTop 和 paddingBottom, 如果设计稿上有上下边距, 只能用 margin 或者再嵌套一层的方式解决.
也许可以写得更完善些.
在生产环境中使用时, 有同学发现该 TextView 中 ClickableSpan 的点击事件无法被触发.
还木有解决...
@Uraka.Lee
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/7064.html
阅读 654·2023-04-25 19:43
阅读 3834·2021-11-30 14:52
阅读 3711·2021-11-30 14:52
阅读 3775·2021-11-29 11:00
阅读 3732·2021-11-29 11:00
阅读 3789·2021-11-29 11:00
阅读 3516·2021-11-29 11:00
阅读 5933·2021-11-29 11:00