shape 大家应该都了解,一种 android 提供的,方便的在 android 绘制生成简单图形的 xml 文件,常见的 shape xml 文件如下:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:innerRadiusRatio="5"
android:shape="ring"
android:thicknessRatio="5"
android:useLevel="false"
>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
<solid android:color="@color/colorPrimary"></solid>
</shape>
就是这么简单的一个 xml 文件,通过 shape 支持的 xml 标签,我们可以使用 shape 绘制出很多常用的图形,我们常常使用的 button 的背景都是用 shape 的绘制的,若是我们使用 png 代替 shape ,那么就会出很多问题的
shape 有着非常鲜明的优点,明显就是为了专门解决依赖一类需求,简单多变的多边形图案的绘制:
- shape 支持宽高的无限拉伸,不存在 png 图片拉伸的问题
首先 shape 不是 bitmap 位图,是我们通过 xml 文件声明号绘制信息,然后 shape 根据所在 view 的尺寸动态绘制出来的,和 SVG 矢量图有异曲同工之妙,只不过没有 SVG 这么强大罢了 - shape 文件很小,节约 apk 安装文件大小,也能节约内存
因为 shape 是通过 canvas 绘制出来的,所以不存在 bitmap 位图来占用大量内存
别看 shape 大家常用,但是 shape 支持很多标签和绘制技巧,这块很多同学就不是那么清楚了,用好 shape 真的能省我们很多因思考死的脑细胞,也能让 UI 少出一些图
1. shape 标签
shape 支持的基础标签不多,但是这每一个基础标标签中都支持很多属性设置,正式通过这些属性变化,绘制出一个个看似很复杂的图形出来,对于这几个基础标签我们来一个一个的看
shape 详细属性如下,不包含根标签
1.1 shape 支持的基础标签
shape 支持根标签,大小,边框,边框圆角,填充色,渐变色 这6个标签,6个标签相互配合,每个标签中还有细分属性。当然了这 6个标签是干什么的很好理解,一来见名知已,二来也是大家经常使用的
根本标签
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
大小
<size></size>
边框
<stroke></stroke>
填充色
<solid></solid>
内边距
<padding></padding>
边框圆角
<corners></corners>
渐变色
<gradient></gradient>
</shape>
1.2 shape 支持的 <shape> 根标签
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle|line|oval|ring"
分别为矩形、线、椭圆、环。默认为矩形rectangle
android:innerRadius="integer"
shape为ring时可用,内环半径
android:innerRadiusRatio="float"
shape为ring时可用,内环的厚度比,即环的宽度比表示内环半径,默认为3,可被innerRadius值覆盖
android:thickness="integer"
shape为ring时可用,环的厚度
android:thicknessRatio="float"
shape为ring时可用,环的厚度比,即环的宽度比表示环的厚度,默认为9,可被thickness值覆盖
android:tint="color"
给shape着色
android:tintMode="src_in|src_atop|src_over|add|multiply|screen"
着色类型
android:useLevel="false|true"
较少用,一般设为false,否则图形不显示。为true时可在LevelListDrawable使用
android:visible="false|true"
android:dither="false|true"
将在位图的像素配置与屏幕不同时(例如:ARGB 8888 位图和 RGB 565 屏幕)启用位图的抖动;值为“false”时则停用抖动。默认值为 true。
>
</shape>
我们需要注意的属性是 <shape>,该属性决定了本 shape 文件的形状,支持4种基础形状:
- rectangle / 矩形
- line / 线
- oval / 椭圆
- ring / 圆环
可能 line 线的效果不是我们预想的那样,这个问题留待以后补充
善用这4种图形,结合其他属性设置,我们基本能画出常用的所有图形,比如椭圆在正方形的尺寸中就是一个圆,矩形加上圆角就是圆角矩形
剩下的其他参数:
- tint / tintMode 是着色器,能不能在 API 19 上使用,我也没试
- dither 一般也没见人写过
- innerRadius / thickness 是画圆环的参数,一个内圆半径,一个外圆宽度
- innerRadiusRatio / thicknessRatio 这是内圆半径和外圆宽度的比例,默认 3:9,测试过发现反过来写才能是我们理解的正常显示
1.3 corners支持的基础标签
corners 这个大家最熟悉了吧,圆角,配合 rectangle / 矩形 来使用了,我们使用 corners 圆角 可以画出圆角矩形来:
- android:radius="integer"
圆角半径,该值设置时下面四个属性失效 - android:bottomLeftRadius="integer"
左下角圆角半径 - android:bottomRightRadius="integer"
右下角圆角半径 - android:topLeftRadius="integer"
左上角圆角半径 - android:topRightRadius="integer"
右上角圆角半径
话说,为啥圆角的此存设置只有一个数值呢,按照 canvas 绘制圆角的思路,应该是有 x,y 2个参数来描述这个圆角的圆角有多大的,为啥这里就一个参数呢。只能这样理解了,shape 的圆角默认就是正方形尺寸的
1.4 padding 支持的基础标签
- android:bottom="integer"
设置底部边距 - android:left="integer"
左边边距 - android:right="integer"
右边 - android:top="integer"
顶部
1.5 solid 支持的基础标签
- android:color="color"
shape的填充色
填充色不用说了吧,不过有一点要说明下,要是在 shape 根标签里设置了 tint 着色器,那么就会覆盖 solid 的颜色
1.6 size支持的基础标签
- android:height="integer"
宽度 - android:width="integer"
高度
shape 一般都不指定具体的大小,因为大多数时候 shape 是作为view 的背景来存在的,此时 shape 的大小是随着 view 的大小走的。
但是有的时候 shape 无法从外接获取准确的大小参数,那么这个时候就需要 shape 自己指定大小了,比如我们在很多时候画圆和圆环当图片使用的时候,就必须指定大小了
但是若是 shape 既设置 size 了,外界页存在依附的 view 的情况下,view 的大小怎么样呢:
view 的 size = wrap_content
此时 view 的最终大小跟着 shape 的大小走view 的 size 有指定大小时,比如 100dp
此时 shape 不影响 view 的最终大小
1.7 stroke 支持的基础标签
- android:color="color"
描边的颜色 - android:width="integer"
描边的宽度 - android:dashGap="integer"
虚线间隔 - android:dashWidth="integer"
虚线宽度
边框不用说了吧没,注意其中虚线的属性
1.8 gradient 支持的基础标签
找到一篇配图很准的,渐变可以参考这篇:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="integer"
渐变角度,当上面type为线性渐变linear时有效。角度为45的倍数,0度时从左往右渐变,角度方向逆时针
android:startColor="color"
渐变开始位置颜色
android:centerColor="color"
渐变中间位置颜色
android:endColor="color"
渐变结束位置颜色
android:centerX="float"
放射中心坐标
android:centerY="float"
放射中心坐标
android:gradientRadius="integer"
type为放射性渐变radial时有效,渐变的半径
android:type="linear|radial|sweep"
渐变类型,分别为线性、放射性、扫描性渐变,默认为线性渐变linear
android:useLevel="false|true"
与上面shape中该属性的一致
/>
</shape>
渐变是 shape 中的难点,很多人其实对渐变的设置很不熟悉,其实各个平台工具中渐变的设置都差不多,shape 中的渐变熟悉了,看其他的也大体能明白,所谓一法通万法通
什么是渐变,就是从一种颜色匀速过渡到另一种颜色,自行实现可以用 android 中的 color 插值器。这样肯定就有2个颜色,一个是开始的颜色,一个是结束的颜色,shape 还支持中间颜色,就是3色过渡
这样就能理解 startColor 、 centerColor 、 endColor 是干啥的了
另外支持3种渐变算法 type
- linear - 线性
- radial - 扫描性
- sweep - 放射性
- 默认是线性渐变 inear
对于这3种渐变算法,来个图最合适:
一图在手,胜似千言万语,大家注意期中颜色变化的起始方向和顺序
- linear
线性这个好理解把,就是一条线,从左到右变化 - radial - 扫描性
也叫雷达图,按照圆心旋转 - sweep - 放射性
从内到外的变化
图中 radial 、sweep 都是从中间开始变化的,这是因为我们可以指定中心点,这里我们指定的是圆心
。这2个参数就是 centerX / centerY 了,取值区间[0,1],默认为0.5,即中心位置
另外还支持 centerColor 的便宜位置,默认 centerColor 肯定是在中间的,我们还可以动态设置 centerColor 在任何位置,这个属性就是 gradientRadius 了,其取值范围和动画 xml 取值一样:
- 30dp,就是 30dp
- 30%,图形的 30% 处
angle 属性是旋转角度,只能以 90 的倍数设置,90 代表旋转 90度,android 中是以逆时针旋转为准的
1.9 所有标签
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="false|true" //将在位图的像素配置与屏幕不同时(例如:ARGB 8888 位图和 RGB 565 屏幕)启用位图的抖动;值为“false”时则停用抖动。默认值为 true。
android:shape="rectangle|line|oval|ring"//分别为矩形、线、椭圆、环。默认为矩形rectangle
android:innerRadius="integer" // shape为ring时可用,内环半径
android:innerRadiusRatio="float" // shape为ring时可用,内环的厚度比,即环的宽度比表示内环半径,默认为3,可被innerRadius值覆盖
android:thickness="integer" // shape为ring时可用,环的厚度
android:thicknessRatio="float" // shape为ring时可用,环的厚度比,即环的宽度比表示环的厚度,默认为9,可被thickness值覆盖
android:tint="color" // 给shape着色
android:tintMode="src_in|src_atop|src_over|add|multiply|screen" // 着色类型
android:useLevel="false|true" // 较少用,一般设为false,否则图形不显示。为true时可在LevelListDrawable使用
android:visible="false|true"
>
<!-- 圆角 -->
<corners
android:radius="integer" // 圆角半径,该值设置时下面四个属性失效
android:bottomLeftRadius="integer" // 左下角圆角半径
android:bottomRightRadius="integer" // 右下角圆角半径
android:topLeftRadius="integer" // 左上角圆角半径
android:topRightRadius="integer" // 右上角圆角半径
/>
<!-- 渐变 -->
<gradient
android:useLevel="false|true" // 与上面shape中该属性的一致
android:type="linear|radial|sweep" // 渐变类型,分别为线性、放射性、扫描性渐变,默认为线性渐变linear
android:angle="integer" // 渐变角度,当上面type为线性渐变linear时有效。角度为45的倍数,0度时从左往右渐变,角度方向逆时针
android:centerColor="color" // 渐变中间位置颜色
android:startColor="color" // 渐变开始位置颜色
android:endColor="color" // 渐变结束位置颜色
android:centerX="float" // type为放射性渐变radial时有效,设置渐变中心的X坐标,取值区间[0,1],默认为0.5,即中心位置
android:centerY="float" // type为放射性渐变radial时有效,设置渐变中心的Y坐标,取值区间[0,1],默认为0.5,即中心位置
android:gradientRadius="integer" // type为放射性渐变radial时有效,渐变的半径
/>
<!-- 内边距 -->
<padding
android:bottom="integer" // 设置底部边距
android:left="integer" // 左边边距
android:right="integer" // 右边
android:top="integer" // 顶部
/>
<!-- 大小 -->
<size
android:height="integer" // 宽度
android:width="integer" // 高度
/>
<!-- 填充 -->
<solid
android:color="color" // shape的填充色
/>
<!-- 描边 -->
<stroke
android:color="color" // 描边的颜色
android:width="integer" // 描边的宽度
android:dashGap="integer" // 虚线间隔
android:dashWidth="integer" // 虚线宽度
/>
</shape>
2. shape 经典实战
上面讲解 xml 属性时我也没贴具体的 shape 例子,就是好放到这里,搜集了一些 shape 的典型应用,应该差不多全了,大伙忘了的来这里找找
2.1 shape 画虚线
shape 虚线的 xml 很简单
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line"
android:useLevel="false">
<stroke
android:width="5px"
android:color="@color/colorPrimary"
android:dashGap="5dp"
android:dashWidth="5dp"></stroke>
</shape>
上面的几个参数就能搞定,但是 shape 画虚线里面很几个坑,膈应人极了
- view 的 height 高度必须大于stroke 的 width 宽度,等于都不行,像上面虚线宽 5px,我给 view height 设 5px 都不行,必须大于 5px 才行,我给的 6px,另外 wrap_content 也不行,要不显示不出来
- 4.0 以上手机,默认使用硬解码, 虚线在实机会显示实现,必须给相关的 view 设置成软解才行 layerType="software"‘
<View
android:layout_width="match_parent"
android:layout_height="6px"
android:layerType="software"
android:background="@drawable/shape_dash"></View>
画实现和虚线的注意点一样,区别就在于虚线的几个参数上
2.2 shape 画圆角矩形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="10dp"></corners>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
<size android:width="20dp" android:height="20dp"></size>
</shape>
圆角矩形没什么可说的,都是标准配参数就行了
2.3 shape 画圆形
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/white"></solid>
<stroke android:width="1dp" android:color="@color/colorPrimary"></stroke>
</shape>
还记得 oval 画的是什么吗,是椭圆,前面说过,正方形画椭圆就是正圆,所以只要能保证尺寸是正方形就行了,可以给外层 view 设置同样的 宽高,也可以在 shape 里面写 size 设宽高,不过一般做背景时我们都是不写 size 的
2.4 shape 画圆环
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:useLevel="false">
<solid android:color="@color/colorPrimary"></solid>
</shape>
画圆环时前面说过外环和内圆默认是 3:9 的关系,我们不改就是这样的
蛋疼呢,shape 的圆环也是几个注意要点:
- 注意必须写 useLevel="false" ,否则不显示
- thickness 、 innerRadius 只支持实际单位,不支持诸如 30% 这样的相对参数,毕竟后面还有thicknessRatio 、innerRadiusRatio 这样的比例参数呢,另外 thickness 、 innerRadius 要是设置了实际大小,比如 50dp,那么不论附着的外层 view 有多大,圆环都只能按照 50dp 显示,除非我们作为 drawable 使用,否则不要写这 thickness 、 innerRadius 2个参数
2.5 shape 画渐变色
用上面圆环的例子来一个
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:useLevel="false">
<gradient
android:type="sweep"
android:centerColor="@android:color/white"
android:centerX="0.5"
android:centerY="0.5"
android:endColor="@android:color/holo_blue_light"
android:startColor="@android:color/holo_red_light"/>
</shape>
是不是挺好看的,早先系统默认的 loading 那个转圈的就是用 shape 写的
需要注意的是:
- 加了渐变色,就不能在写填充色了,要不就覆盖了,什么也看不出来了
2.6 shape 画单边框
就是利用 layer-list 图层来做,2图,底下一层是边框颜色图层,上面是背景色图层,通过移动上面背景色图层来把下面边框颜色图层让出一部分出来,形成边框
效果是这样子的:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@android:color/holo_red_light"></solid>
</shape>
</item>
<item android:bottom="1dp">
<shape>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
这里是利用 item 的 位移属性来做的,top 就是向上移动,top = 1dp,就是给下面图层让出 1dp 出来,这就成了边框。
当然这是单边框,我们还可以给其他边设置位移,形成任意数量边框的图
试试3个边框的:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="@android:color/holo_red_light"></solid>
</shape>
</item>
<item android:bottom="1dp" android:left="1dp" android:right="1dp">
<shape>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
上面是设置的正数,我们还可以设置负数,思路和上面本质上都是一样,区别是2个图层需要倒过来,具体就不写代码了。
2.7 shape 画角标图
经典的显示消息数量的角标图
效果图:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<stroke android:width="1dp" android:color="@android:color/white"></stroke>
<solid android:color="@android:color/holo_orange_dark"></solid>
</shape>
当然为了显示精确还是推荐使用自定义 view 来做,因为不同设备子的大小有差异,可能会出现文字除了背景图的问题,自己有用 canvas 就没有这个问题,还能根据文字的多少决定是圆形的还是长条圆角的,有的啃爹产品让显示 999+ ,明显圆形就放不下4个字了,能放下4个字圆也会太大显得突兀
2.8 shape 画阴影
shape 画阴影和单边框一个原理,都是移动图层,形成视觉差,我找到一个图解,看着比较容易理解:
然后咱自己画一个,可以选择给一边记上边框,这样显得更显眼一点
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="2dp" android:left="2dp">
<shape>
<solid android:color="@android:color/darker_gray"></solid>
</shape>
</item>
<item android:right="2dp" android:bottom="2dp">
<shape>
<stroke android:width="1dp" android:color="#f2f2f2"></stroke>
<solid android:color="@android:color/white"></solid>
</shape>
</item>
</layer-list>
以前有个很流行的交互效果没,就是按钮点下后出阴影,就是把做了2张 shape 图,一个有阴影,一个不带,然后放到 selector 里面。说实话以前我都不知道是怎么实现的,现在回过头再来看,真是简简单单啊,说明系统知识一定要全面啊,要不有时候费死劲都不知道怎么实现啊