转载地址:http://www.jianshu.com/p/a3cb1e23c2c4#rd
Android Support Library 23.2 出来以后,在Android 5.0(API级别21)以前的系统中,也可以定义矢量drawables,即VectorDrawable。它可以在不失清晰度的情况下进行缩放。你仅仅需要需要一个矢量图片的资源文件,而不再需要为每个屏幕密度设置一个资源文件,在一定程度上可以减小项目的体积。
vector 标签下的最主要就是 pathData,其实pathData跟Android中 Path api对路径的定义规则是差不多的,当你掌握了 pathData 的语法,同时有一颗不轻易放弃的心,就可以通过一些简洁的指令完成几乎所有的图案。
越是复杂的东西往往越有规律可循。下面就来详细说说pathData 的语法。给我五!
<!--上面的手掌对应的代码实现-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="
M22,23 q0,4 -4,4 h-7 q-2,0 -3,-1 T1,16 q-0.6,-0.8 0,-2 t5,3
q1,1 2,0 T8,4 q0,-1 0.9,-1.1 t1.1,1 1.5,9 q0.25,0.5 0.5,0.5
t0.5,-0.5 0,-11 q0.2,-1 1.1,-1.1 t1.1,1.1 1,11 q0.25,0.5 0.5,0.5
t0.5,-0.5 0.5,-9 q0.2,-1 1,-1 t1,1 0.5,9 q0.25,0.5 0.5,0.5
t0.5,-0.5 1.2,-6.5 q0.3,-1 1,-1 t0.8,1 -0.8,6 T22,23"/>
</vector>
基本规则
pathData 的指令基本都是由字母跟若干数字组成,数字之间可以用空格或者逗号隔开 (其实逗号会被忽略掉,加上逗号只是一些习惯的问题)。一般来说指令字母分为大小写两种,大写的字母是基于原点的坐标系(偏移量),即绝对位置;小写字母是基于当前点坐标系(偏移量),即相对位置。
移动
- M x,y (m dx, dy) 移动虚拟画笔到对应的点,但是并不绘制。一开始的时候默认是在(0,0)。
直线
- L x,y (l dx, dy) 从当前点划一条直线到对应的点。
- H x (h dx) 从当前点绘制水平线,相当于l x,0
-
V y (v dy) 从当前点绘制垂直线,相当于l 0,y
<!--下面的例子除了 pathData, 其他跟此文件一样-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#0000"
android:strokeColor="#000"
android:strokeWidth="0.2"
android:pathData=" M10,10 L10,15 L15,15 L10,10"/>
</vector>
将上述代码 android:pathData=" M10,10 L10,15 L15,15 L10,10"
替换成以下代码效果相同
android:pathData="M10,10 l 0,5 l 5,0 l-5,-5"
android:pathData="M10,10 V 15 H 15 L10,10"
android:pathData="M10,10 v 5 h 5 l-5,-5"
闭合
- Z(或z) 从结束点绘制一条直线到开始点,闭合路径
上面的图形型也可以由以下代码绘制android:pathData="M10,10 v 5 h 5 z"
弧线
- A rx,ry x-axis-rotation large-arc-flag,sweepflag x,y
-
a rx,ry x-axis-rotation large-arc-flag,sweepflag dx,dy
rx ry 椭圆半径
x-axis-rotation x轴旋转角度
large-arc-flag 为0时表示取小弧度,1时取大弧度(要长的还是短的)
sweep-flag 0取逆时针方向,1取顺时针方向
x,y (dx,dy) 终点的位置
这个弧线的指令比起直线就相对复杂得多了,7个参数容易搞混了。来看个例子android:pathData="M8,10 a4,6 0 1,1 6 6"
红色(8,10) 是起点, X轴旋转的角度为0,取大弧,顺时针,蓝色(14,16) 是终点。
- 只将x-axis-rotation 改为30跟-30分别对应下面左边跟右边的效果,可见正数是按顺时针旋转负数按逆时针旋转。
x-axis-rotation为30(左)、-30(右)
- 只将large-arc-flag 改为 0
android:pathData="M8,10 a4,6 0 0,1 6 6"
对应下图效果
- 只将sweep-flag改为 0
android:pathData="M8,10 a4,6 0 1,0 6 6"
对应下图效果
网上看到一张图,基本总结了弧线
二阶贝塞尔曲线
- Q x1,y1 x,y ( q dx1,dy1 dx,dy)
- T x,y ( t dx, dy)
先来看看二阶贝赛尔曲线的公式
没想到高数(高中数学)考过149分的我也研究不透这个鬼,还是看看动画吧
一下子感觉清晰了不少,对于Q指令来说(x1,y1)就是P1这个控制点,(x,y)就是终点P2。P0当然就是执行指令前最后的位置。这里有一点需要说明的, q dx1,dy1 dx,dy 这个指令,两个控制点都是相对P0点的。
而T指令又是什么鬼?为什么只有两个参数?其实T指令是在你画完一条贝塞尔曲线后,只需用T指令指定终点,就能画出一条平滑的贝塞尔曲线。控制点被默认为上一次的控制点关于上次终点的中心对称点。比如上次的控制点P1是(6,6),终点P2是(8,10),
那么使用T指令后默认控制点P1`为(10,14)。
举个例子:
android:pathData="M4,10 Q6,6 8,10 Q10,14 12,10"
android:pathData="M4,10 Q6,6 8,10 T12,10"
android:pathData="M4,10 q2,-4 4,0 q2,4 4,0"
android:pathData="M4,10 q2,-4 4,0 t4,0"
这四个对应的都是下面的曲线
三阶贝塞尔曲线
- C x1,y1 x2,y2 x,y ( c dx1,dy1 dx2,dy2 dx,dy)
- S x1,y1 x,y ( s dx1,dy1 dx, dy)
同样,先列下公式
这玩意估计也看不懂,还是结合动画来吧
跟二阶类似,但是三阶有两个控制点,分别是P1(x1,y1),P2(x2,y2)还有终点P3(x,y)。类似的, c dx1,dy1 dx2,dy2 dx,dy 所有的点都是相对于P0,即上一次的终点。
S指令跟T指令类似。S指令相对于C指令少了一个控制点,这个控制点就是上一次最后一个控制点相对上次的终点的中心对称点。还是同样举个例子:
android:pathData="M4,10 C6,6 8,14 10,10 C12,6 14,14 16,10"
android:pathData="M4,10 C6,6 8,14 10,10 S14,14 16,10"
android:pathData="M4,10 c2,-4 4,4 6,0 c2,-4 4,4 6,0"
android:pathData="M4,10 c2,-4 4,4 6,0 s4,4 6,0"
以上四个均会出现下图的效果
实践
终于把所有的指令搞清楚啦,那么接下来就来画一个比较简单的图案吧(为了一次性涉及几乎所有的指令,大家就别在意图画的很丑啦)
首先分析一下这个图的构成。它是一个圆角正方形同时里面有一个镂空的心型。先来看看正方形
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:fillColor="#f24e4e"
android:pathData="M8,4
h24 q4,0 4,4 v24 q0,4 -4,4
h-24 q-4,0 -4,-4 v-24 q0,-4 4,-4"/>
</vector>
细心的同学应该注意到这里没有设置strokeColor,strokeWidth,并且fillColor也不再设置透明了,所以出现的是没有边框的填充效果。上面viewportWidth为40,则把这张图分成40个单位。
在pathData中,首先移动到A(8,4),然后水平移动了24个单位到B(32,4)。接着就是一条二阶贝塞尔曲线,起点是B,控制点是C(36,4),终点是D(36,8)。然后再画一条垂直线到(36,32)…下面的操作都是差不多,就不多说的。这里注意一点,画这个正方形是从A-B-C-D,也就是顺时针的方向,这点对接下来画心型很重要。
画完了圆角正方形,接下来就要画一个镂空的心型。有同学说再加上一个path标签,设置不同颜色就可以。这方法乍一看挺不错,但是他实现的并不是镂空的效果,而是叠加的效果。镂空的话中间的心型是透明的,但是叠加就没办法让中间的心呈透明。
那怎么实现镂空的效果呢?其实关键就是前面说的方向。前面的正方形是顺时针的方向,如果我们在pathData 后面加上另一逆时针方向的路径,就会出现取反的效果,比如
<path
android:fillColor="#f24e4e"
android:pathData="M8,4
h24 q4,0 4,4 v24 q0,4 -4,4
h-24 q-4,0 -4,-4 v-24 q0,-4 4,-4
M2,20 h20 v-10 z"/>
在刚才的基础上加上M2,20 h20 v-10 z
,这是一个逆时针方向的三角形,会变成下面这样
也就是说,我们只需要画一个逆时针方向的心型,就会出现上面的那个效果。先看看代码
<path
android:fillColor="#f24e4e"
android:pathData="M8,4
h24 q4,0 4,4 v24 q0,4 -4,4
h-24 q-4,0 -4,-4 v-24 q0,-4 4,-4
M20,15
a5,6 -15 0 0 -9,2
c0,5 4,6 9,12
c5,-6 9,-7 9,-12
a5,6 15 0 0 -9 -2"/>
这个是在正方形的基础上加上
M20,15
a5,6 -15 0 0 -9,2
c0,5 4,6 9,12
c5,-6 9,-7 9,-12
a5,6 15 0 0 -9 -2
我们看看如果pathData 单独设为上述路径会是什么样的
果然很漂亮。首先它从白色的点
(20,15)开始,向逆时针方向,也就是右边画了一段圆弧到蓝色的点(11,17),圆弧所在的椭圆逆时针转15°,取短弧,逆时针。然后再画一条三阶贝塞尔曲线,控制点分别是黄点(11,22),黑点(15,23),终点是绿色的点(20,
29)。这样子就完成了心型的一半了。另一半跟这边的完全对称,也就不细说了。
完整代码
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="40"
android:viewportHeight="40">
<path
android:fillColor="#f24e4e"
android:pathData="M8,4
h24 q4,0 4,4 v24 q0,4 -4,4
h-24 q-4,0 -4,-4 v-24 q0,-4 4,-4
M20,15
a5,6 -15 0 0 -9,2
c0,5 4,6 9,12
c5,-6 9,-7 9,-12
a5,6 15 0 0 -9 -2"/>
</vector>
一些废话
网上很多介绍VectorDrawable,但是介绍pathData的真的有点少。我也是通过查阅大量的资料,加上实践,基本算对pathData语法入门了吧,这东西掌握了真的不难,还可以装逼哈哈。这篇博客写的时间真的有点长,整整一天,第一写很多不懂的,希望大家多多指教。有机会再写一下Animated Vector Drawables。
原文链接:http://www.jianshu.com/p/a3cb1e23c2c4#rd
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。