在把一个 Android 项目从兼容高版本向兼容低版本反向移植时,遇到一个小问题,那就是 XML 格式的 drawable 资源里的 vector
元素不被接受。原始数据如下,在 1.0 的 Android Studio 的资源编辑器中报错,而老夫也没有去高版本的 Android Studio 里去看它究竟是什么样子。理论上来说,应该是竖着排列的三个小点,以表示“More”的语义。
1 2 3 4 5 6 7 |
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0"> <path android:fillColor="#FF000000" android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/> </vector> |
但毕竟也不愿用一个光栅图片来代替它,于是决定用 Shape 元素来达到相同的(想象中的)效果。一个资源文件中,不使用组合技,貌似不能有多个 shape
元素,经过考察,感觉 layer-list
应该可以承担这使命,老夫是将其认作为 FrameLayout 来理解的。最开头是这样写的(因为了解到 shape
元素是有 padding 属性的,所以想当然地写出了这个版本以作后续微调的基础):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#FF0000" /> </shape> </item> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#00FF00" /> </shape> </item> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#0000FF" /> </shape> </item> </layer-list> |
三个圆(尽管 shape
的类型是 oval
,即椭圆,但宽高相等控制其表现为正圆)相互重叠,视觉上只能看到最前一层(也即最后一个 shape
),其为蓝色。后续的微调思路很明确,把三个圆的上下 padding 设置好,理应可以完美错开,分布为从上到下的三个圆。所以,三个圆的 padding 设置应该是:left/right 均无需设置,而将 top 依次设置为 4dp、24dp、44dp,即如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#FF0000" /> <padding android:top="4dp" /> </shape> </item> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#00FF00" /> <padding android:top="24dp" /> </shape> </item> <item> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#0000FF" /> <padding android:top="44dp" /> </shape> </item> </layer-list> |
如果正在浏览本文的你富于求真的精神,不妨自己打开 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
元素进行了设置,如下(调试阶段结束,各个圆的颜色从红绿蓝统一为黑色):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:top="0dp" android:bottom="40dp"> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#000000" /> </shape> </item> <item android:top="20dp" android:bottom="20dp"> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#000000" /> </shape> </item> <item android:top="40dp" android:bottom="0dp"> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="16dp" android:height="16dp" /> <solid android:color="#000000" /> </shape> </item> </layer-list> |
附录
这几篇文章,相对比较图文并茂地介绍了 shape 元素的各种可选属性及其表现: