xml_绘图: shape 篇

作者: 前行的乌龟 | 来源:发表于2018-08-29 08:43 被阅读291次

    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 详细属性如下,不包含根标签


    5445868-6ce6de5f5d971e71.png

    1.1 shape 支持的基础标签

    shape 支持根标签,大小,边框,边框圆角,填充色,渐变色 这6个标签,6个标签相互配合,每个标签中还有细分属性。当然了这 6个标签是干什么的很好理解,一来见名知已,二来也是大家经常使用的


    Snip20180823_2.png
    根本标签
    <?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> 根标签

    Snip20180823_3.png
    <?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 / 圆环
    Snip20180826_5.png

    可能 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种渐变算法,来个图最合适:


    Snip20180828_9.png

    一图在手,胜似千言万语,大家注意期中颜色变化的起始方向和顺序

    • 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>
    
    Snip20180829_10.png

    上面的几个参数就能搞定,但是 shape 画虚线里面很几个坑,膈应人极了

    1. view 的 height 高度必须大于stroke 的 width 宽度,等于都不行,像上面虚线宽 5px,我给 view height 设 5px 都不行,必须大于 5px 才行,我给的 6px,另外 wrap_content 也不行,要不显示不出来
    2. 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>
    
    Snip20180829_11.png

    圆角矩形没什么可说的,都是标准配参数就行了

    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>
    
    Snip20180829_13.png

    还记得 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>
    
    
    Snip20180829_14.png

    画圆环时前面说过外环和内圆默认是 3:9 的关系,我们不改就是这样的

    蛋疼呢,shape 的圆环也是几个注意要点:

    1. 注意必须写 useLevel="false" ,否则不显示
    2. 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>
    
    Snip20180829_15.png

    是不是挺好看的,早先系统默认的 loading 那个转圈的就是用 shape 写的

    需要注意的是:

    • 加了渐变色,就不能在写填充色了,要不就覆盖了,什么也看不出来了

    2.6 shape 画单边框

    就是利用 layer-list 图层来做,2图,底下一层是边框颜色图层,上面是背景色图层,通过移动上面背景色图层来把下面边框颜色图层让出一部分出来,形成边框

    效果是这样子的:


    Snip20180829_1.png
    <?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个边框的:


    Snip20180829_2.png
    <?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 画角标图

    经典的显示消息数量的角标图

    效果图:


    Snip20180829_4.png
    <?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 画阴影和单边框一个原理,都是移动图层,形成视觉差,我找到一个图解,看着比较容易理解:


    Snip20180829_6.png

    然后咱自己画一个,可以选择给一边记上边框,这样显得更显眼一点

    Snip20180829_8.png
    <?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 里面。说实话以前我都不知道是怎么实现的,现在回过头再来看,真是简简单单啊,说明系统知识一定要全面啊,要不有时候费死劲都不知道怎么实现啊

    参考文章:


    相关文章

      网友评论

      • GoFly233:“什么是渐变,就是从一种颜色匀速过度到另一种颜色”

        “过渡”写错了啊,写成了“过度”
        前行的乌龟:@GoFly233 app 端有相关推送,看到了手头方便一般都立马回
        GoFly233:@前行的乌龟 妈耶,秒回,好怕怕
        前行的乌龟::cry: 稍后修改,感谢认真观看~,你这是我碰到的最认真的读者了

      本文标题:xml_绘图: shape 篇

      本文链接:https://www.haomeiwen.com/subject/reamiftx.html