本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!
前言
很多语言和文字拥有特殊的、复杂的写法、画法,一个字符可能延伸到前一个字符的区域,甚至后一个字符的区域。
如果文字的宽度没有做针对这种文字做额外的加宽处理,那么文字整体在边界区域会出现被切掉的现象。
比如如下的文字类型,最右边的文字 ร์ 的右上角的符号没有显示完全。
针对这个痛点,Android 15 进行了优化,如果 app 面向 Android 15 及更高的版本后,可以采用 setUseBoundsForWidth()
设置为 true,就可以拓宽 TextView 的显示区域。
可以看到,采用新设置之后,最右边的文字(ร์)的右上角的符号能完全显示了。
说明
宽度的调整会给已有的布局引起垂直方向上不对齐的可能性问题,所以该设置默认是关闭的。
另外,事实上光依赖 setUseBoundsForWidth()
还不够,还需要将 shiftDrawingOffsetForStartOverhang
设置为 true,那这个 API 干啥用的,我们在后面的实战环节会进行介绍。
除了 set 方法,Android 15 也提供了 getUseBoundsForWidth()
和 getShiftDrawingOffsetForStartOverhang()
供开发者动态地调用。
当然,这两项设置在 XML 中也有相应的 attribute 支持,供开发者在布局中直接使用。
- android:
useBoundsForWidth
bool - android:
shiftDrawingOffsetForStartOverhang
bool
实战
我们以 cursive 字体下的 Java 文字为例,进行该 API 的尝试。
xml
<TextView
android:fontFamily="cursive"
android:text="java" />
默认情况下没有效果,字符 J 的左下角被切掉了。
正如文档所说 Android 15 上 useBoundsForWidth 未指定的情况下,width 不会拓宽。
xml
<TextView
android:fontFamily="cursive"
android:text="java"
android:useBoundsForWidth="true" />
仍然没有效果,因为还要依赖 shiftDrawingOffsetForStartOverhang 属性。
xml
<TextView
android:fontFamily="cursive"
android:text="java"
android:useBoundsForWidth="true"
android:shiftDrawingOffsetForStartOverhang="true"/>
可以看到有效果了,J 字符的左边展示完全了。
Inspecting 检查
让我们 dump 角度 double confirm 下 View 的尺寸上是否有变化。
最简单快速的是使用 LayoutInspector。
通过 inspecting,咱们发现视觉上明明拓宽了的 TextView 在 inspector 里展示的 width 却都是 60dp。
Dumpsys 确认
暂时不确定这是 Inpector 的 bug 还是确实如此,我们尝试用 adb dump 下宽高。
bash
adb shell dumpsys activity top
我们用 adb dump 一下试试。
bash
ACTIVITY com.ellison.osvdemo/.textView.TextViewWidthActivity d53efc9 pid=6004 userId=0 uid=10196 displayId=0(type=INTERNAL)
...
View Hierarchy:
com.android.internal.policy.DecorView{2c56827 V.E...... R....... 0,0-1080,2400 aid=0}[]
android.widget.LinearLayout{8f36ad4 V.E...... ........ 0,0-1080,2400}
android.view.ViewStub{e29447d G.E...... ......I. 0,0-0,0 #10201cb android:id/action_mode_bar_stub}
android.widget.FrameLayout{1e6f72 V.E...... ........ 0,63-1080,2337}
androidx.appcompat.widget.ActionBarOverlayLayout{7165fc3 V.E...... ........ 0,0-1080,2274 #7f080092 app:id/decor_content_parent}
androidx.appcompat.widget.ContentFrameLayout{9866040 V.E...... ........ 0,147-1080,2274 #1020002 android:id/content}
...
com.google.android.material.textview.MaterialTextView{df946ed V.ED..... ........ 461,394-619,539 #7f0801fa app:id/textview4}
com.google.android.material.textview.MaterialTextView{fbebfb3 V.ED..... ........ 449,933-631,1078 #7f0801fb app:id/textview5}
com.google.android.material.textview.MaterialTextView{db064e9 V.ED..... ........ 449,1472-631,1617 #7f0801fc app:id/textview6}
可以看到几个 TextView 在左上右下的坐标:
- 默认情况下的 Java 文字 TextView 的坐标: 461, 394 ~ 619, 539,宽度是 619 - 461 = 158px
- 仅开启 setUseBoundsForWidth 情况下的 Java 文字 TextView 的坐标: 449, 933 ~ 631, 1078,宽度是 631 - 449 = 182px
- 外加开启 shiftDrawingOffsetForStartOverhang 情况下的 Java 文字 TextView 的坐标: 449, 1472 ~ 631, 1617,宽度也是 631 - 449 = 182px
至此,大家应该能得到结论:
setUseBoundsForWidth
实际已经拓宽了宽度,但没有减少文字在 TextView 内部的 padding,导致仍被切掉、显示不全shiftDrawingOffsetForStartOverhang
并没有控制宽度,则是在 draw 的时候向右 offset,促使文字不被切掉
我们将效果截图放大,大家仔细看下:
细致对比之后,可以看出,两者宽度实际上一致,唯一不同的是后者将内容往右进行了平移。
结语
几乎每年的 Android 系统升级,Google 都会针对 TextView 基础组件进行改动或新的支持。
今年的 Android 15 版本也是一样,针对 TextView
进行了 3 处修改:
- 避免文本切断的
setUseBoundsForWidth
API 和shiftDrawingOffsetForStartOverhang
API - 依据 locale 选择适合的 TextView 行高数值的
setLocalePreferredLineHeightForMinimumUsed
API - 设置的字体测量最小值的
setMinimumFontMetrics
API - 针对 TextView 内容换行的专用属性
<nobreak>
API
本文阐述了第 1 点,后续将逐步阐述其他几个变化,敬请期待。