Android 绘制矢量图(下)—— 渐变色、圆形

1,283 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

在上篇文章中,我们学习了 Android 中 SVG 的基本语法。本篇文章来学习一下高级一点的用法:使用 SVG 绘制渐变色和圆形。

一、渐变色的绘制

一张最简单的渐变色 SVG 图片代码如下:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="100dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <path android:pathData="M0,0 H100 V100 H-100z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startColor="#FFFFFF"
                android:endColor="#000000"
                android:startX="0"
                android:startY="0"
                android:endX="100"
                android:endY="100"
                android:type="linear" />
        </aapt:attr>
    </path>
</vector>

效果图:

linear gradient

通过 aapt:attr 指定需要注入属性,name 指定注入的属性名,这里设置为 android:fillColor,表示需要注入的属性是填充色。

然后编写 gradient 标签,设置开始颜色 startColor,结束颜色 endColor,这里指定为从白色到黑色。

然后设置起始点和终点的坐标,这里定义的是从 (0,0) 到 (100,100),也就是从画布左上角到右下角,这条路径的含义是渐变色的变化方向。

为了更清晰地解释渐变色的方向,不妨将起始点和终点修改为 (0,0) 到 (0,100),也就是将这里的 endX 改为 0,效果如下:

vertical_gradient

可以很明显的看出,渐变色的方向由左上到左下变成了从上到下,这就是渐变色方向的含义。

type 属性用于指定渐变色的渐变类型,常用的类型有 linear、radial、sweep,分别表示线型、发散型、扫描型。

如果渐变色只能指定 startColor、endColor,那就只能处理两种颜色之间的渐变,那么多色渐变做起来就会很复杂。所以还有另一种方式指定渐变色的颜色变化,就是给 gradient 标签内部添加 item 标签:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="100dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <path android:pathData="M0,0 H100 V100 H-100z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startX="0"
                android:startY="0"
                android:endX="100"
                android:endY="100"
                android:type="linear">
                <item
                    android:color="#FFFFFF"
                    android:offset="0" />
                <item
                    android:color="#0000FF"
                    android:offset="0.5" />
                <item
                    android:color="#000000"
                    android:offset="1" />
            </gradient>
        </aapt:attr>
    </path>
</vector>

效果如下:

gradient with item

每个 item 标签代表一种颜色,offset 表示这个颜色处于整个渐变区域的哪个位置。取值范围是 0~1,表示整个渐变区域的百分比。

在设置了 item 之后,startColor 和 endColor 就不会起作用了。也就是说 startColor/endColor 和 item 标签是不能混用的。

通过 item 标签,我们将 offset 0 的区域指定为 白色,offset 0.5 的区域指定为蓝色,offset 1 的区域指定为 黑色,就形成了上图中的效果。

再看一个发散型渐变色的例子:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="100dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <path android:pathData="M0,0 H100 V100 H-100z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startColor="#FFFFFF"
                android:endColor="#000000"
                android:centerX="50"
                android:centerY="50"
                android:gradientRadius="50"
                android:type="radial" />
        </aapt:attr>
    </path>
</vector>

效果图:

radial gradient

是不是有种黑洞照片的科幻感 =,=

在这个例子中,type 指定为 radial,表示发散型渐变色。发散型渐变色需要的参数和线型渐变色的参数略有不同。

startColor 和 endColor 表示起始颜色和结束颜色,这一点和线型渐变色是相同的。并且,同样可以通过 item 标签指定多种渐变色。

然后通过 centerX、centerY 指定圆心的坐标,通过 gradientRadius 指定渐变色的半径。这样就能完成上图的效果。

接下来再看一个扫描型渐变色的例子:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="100dp"
    android:height="100dp"
    android:viewportWidth="100"
    android:viewportHeight="100">
    <path android:pathData="M0,0 H100 V100 H-100z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:startColor="#FFFFFF"
                android:endColor="#000000"
                android:centerX="50"
                android:centerY="50"
                android:type="sweep">
            </gradient>
        </aapt:attr>
    </path>
</vector>

效果图:

sweep gradient

在这个例子中,type 指定为 sweep,表示扫描型渐变色。可以看到,扫描型渐变色和发散型渐变色的写法比较类似。

同样地,startColor 和 endColor 表示起始颜色和结束颜色。并且,也可以通过 item 标签指定多种渐变色。

然后再通过 centerX、centerY 指定圆心的坐标即可。

注:线型、射线型、扫描型的名字是我自己翻译的,我没有找到这三种渐变色类型的官方中文译名。建议读者直接使用英文名交流 linear、radial、sweep。

再注:在 Android Studio 中编辑渐变色时,没有代码提示、自动补全的功能,我暂时没有找到解决办法,只能手打一个个属性。

二、圆形的绘制

一个最简单的圆形的代码是:

<path
    android:fillColor="#FFFFFF"
    android:pathData="
    M100,100
    A30,30,0,1,0,99,100
    z" />

效果图:

circle

圆形的绘制使用的是 A 符号,前文中讲过,A 是用来绘制弧线的(arc)。在 SVG 中,并没有专门用于绘制圆形的符号。所以要么通过一条几乎闭合的弧线绘制圆形、要么用两条弧线绘制圆形。

读者可能会有疑问,为什么说是几乎闭合呢?如果将弧线的起点和终点设置成一样,不就能绘制一条完整的圆形了吗?

实际上 A 符号的起点和终点一样时,不会触发 SVG 的绘制。这可能是个 bug,(产品经理小声说道:这其实是个 feature)。或许 A 符号出于安全检查的考虑,为了避免做无用的绘制,将起点和终点一致的弧线直接忽略了。

所以这里的 pathData 中,先移动到 (100,100) 坐标准备绘制,而弧线的终点坐标是 (99,100),终点和起点的横坐标相差 1,目的就是避免 A 符号不触发绘制。所以这里的圆严格意义上来讲不是一个正圆,但肉眼是分辨不出来的。

如果用两条弧线绘制圆形的话,可以保证绘制出的是一个正圆,具体方法是每条弧线只画一个半圆,最后拼接成一个正圆。这种方式稍显复杂,所以我没有采用。

小结

本文学习了三种渐变色的绘制:linear、radial、sweep。以及 A 符号绘制圆形的技巧。

参考文章

Android 绘制矢量图(下)

Android矢量图(一)--VectorDrawable基础

Android矢量图(二)--VectorDrawable所有属性全解析

Android矢量图(三)--VectorDrawable渐变色