安卓开发干货Android 基础知识

xml_绘图: shape 篇

2018-08-29  本文已影响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 大家常用,但是 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种基础形状:

Snip20180826_5.png

可能 line 线的效果不是我们预想的那样,这个问题留待以后补充

善用这4种图形,结合其他属性设置,我们基本能画出常用的所有图形,比如椭圆在正方形的尺寸中就是一个圆,矩形加上圆角就是圆角矩形

剩下的其他参数:

1.3 corners支持的基础标签

corners 这个大家最熟悉了吧,圆角,配合 rectangle / 矩形 来使用了,我们使用 corners 圆角 可以画出圆角矩形来:

话说,为啥圆角的此存设置只有一个数值呢,按照 canvas 绘制圆角的思路,应该是有 x,y 2个参数来描述这个圆角的圆角有多大的,为啥这里就一个参数呢。只能这样理解了,shape 的圆角默认就是正方形尺寸的

1.4 padding 支持的基础标签

1.5 solid 支持的基础标签

填充色不用说了吧,不过有一点要说明下,要是在 shape 根标签里设置了 tint 着色器,那么就会覆盖 solid 的颜色

1.6 size支持的基础标签

shape 一般都不指定具体的大小,因为大多数时候 shape 是作为view 的背景来存在的,此时 shape 的大小是随着 view 的大小走的。

但是有的时候 shape 无法从外接获取准确的大小参数,那么这个时候就需要 shape 自己指定大小了,比如我们在很多时候画圆和圆环当图片使用的时候,就必须指定大小了

但是若是 shape 既设置 size 了,外界页存在依附的 view 的情况下,view 的大小怎么样呢:

1.7 stroke 支持的基础标签

边框不用说了吧没,注意其中虚线的属性

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

对于这3种渐变算法,来个图最合适:


Snip20180828_9.png

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

图中 radial 、sweep 都是从中间开始变化的,这是因为我们可以指定中心点,这里我们指定的是圆心
。这2个参数就是 centerX / centerY 了,取值区间[0,1],默认为0.5,即中心位置

另外还支持 centerColor 的便宜位置,默认 centerColor 肯定是在中间的,我们还可以动态设置 centerColor 在任何位置,这个属性就是 gradientRadius 了,其取值范围和动画 xml 取值一样:

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

参考文章:


上一篇下一篇

猜你喜欢

热点阅读