Android 的 Shape 绘制资源的诡异属性

在把一个 Android 项目从兼容高版本向兼容低版本反向移植时,遇到一个小问题,那就是 XML 格式的 drawable 资源里的 vector 元素不被接受。原始数据如下,在 1.0 的 Android Studio 的资源编辑器中报错,而老夫也没有去高版本的 Android Studio 里去看它究竟是什么样子。理论上来说,应该是竖着排列的三个小点,以表示“More”的语义。

但毕竟也不愿用一个光栅图片来代替它,于是决定用 Shape 元素来达到相同的(想象中的)效果。一个资源文件中,不使用组合技,貌似不能有多个 shape 元素,经过考察,感觉 layer-list 应该可以承担这使命,老夫是将其认作为 FrameLayout 来理解的。最开头是这样写的(因为了解到 shape 元素是有 padding 属性的,所以想当然地写出了这个版本以作后续微调的基础):

三个圆(尽管 shape 的类型是 oval,即椭圆,但宽高相等控制其表现为正圆)相互重叠,视觉上只能看到最前一层(也即最后一个 shape),其为蓝色。后续的微调思路很明确,把三个圆的上下 padding 设置好,理应可以完美错开,分布为从上到下的三个圆。所以,三个圆的 padding 设置应该是:left/right 均无需设置,而将 top 依次设置为 4dp、24dp、44dp,即如下:

如果正在浏览本文的你富于求真的精神,不妨自己打开 Android Studio 试着看一眼效果。假使你并非提前专研过本课题,相信你一定会大吃一惊,至少我本人是如此。

在经过许多次的修改->观察->思考的轮回后,我大致摸索出了 padding 的真实作用:前一 shape 的 padding 设置,会作用于尾随其后的其它 shape 上,而且,设置的数值是相对于前一 shape 的边界而言,而非 layer-list 的整体边界。如果后续 shape 大小的总计(包括 padding 在内)超出了前面 shape 的大小的设置值,则设置的值会失效,会被扩展到刚刚好。

以上例来说,尽管每一个 shape 都写明了其高度为 16dp,但真正的高度是需要综合计算的。第三个是最后一个 shape,从它算起,它就是自己,所以它的高度是 16dp;接着向前推算第二个 shape 的高度。第二个 shape 设置了 top padding 为 24dp,也就是说,第三个 shape 在纵向上开始于第二个 shape 内的偏移 24dp 处,所以第二个 shape 自己设定的 16dp 已经无法适用,它的真实高度取可容纳的最小值,也就是 24dp 加上第三个 shape 的 16dp,即 40dp。同理,第一个 shape 的高度就成了第二个 shape 的高度 40dp 加上第二个 shape 在第一个 shape 里的纵向起始偏移 4dp,为 44dp。

在横向上,观察到的结果似乎是,layer-list 的一系列 shape,哪个 shape 的宽度最大,则会成为所有 shape 的宽度(含 padding),有一种拉伸的作用。

由于 padding 的这种诡异表现,老夫最终的版本其实放弃了使用 padding 来进行布局,而是对 item 元素进行了设置,如下(调试阶段结束,各个圆的颜色从红绿蓝统一为黑色):

附录

这几篇文章,相对比较图文并茂地介绍了 shape 元素的各种可选属性及其表现:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注