Vector使用前言
如果要说Vector,就不得不提SVG了,首先,需要讲解两个概念——SVG和Vector。
SVG,即Scalable Vector Graphics 矢量图,这种图像格式在前端中已经使用的非常广泛了。
Vector,在Android中指的是Vector Drawable,也就是Android中的矢量图,因此,可以说Vector就是Android中的SVG实现,因为Android中的Vector并不是支持全部的SVG语法,也没有必要,因为完整的SVG语法是非常复杂的,但已经支持的SVG语法已经够用了,特别是Path语法,几乎是Android中Vector的标配。
更多SVG的介绍,可以看我的另一篇博客Android里的SVG,这里就不对SVG作过多介绍了。
Vector优势
Vector Drawable相对于普通的Drawable来说,有以下几个好处:
- Vector图像可以自动进行适配,不需要通过分辨率来设置不同的图片
- Vector图像可以大幅减少图像的体积,同样一张图,用Vector来实现,可能只有PNG的几十分之一
- 使用简单,很多设计工具,都可以直接导出SVG图像,从而转换成Vector图像
- 功能强大,不用写很多代码就可以实现非常复杂的动画 成熟、稳定,前端已经非常广泛的进行使用了
Vetor语法介绍
Android以一种简化的方式对SVG进行了兼容,这种方式就是通过使用它的Path标签,通过Path标签,几乎可以实现SVG中的其它所有标签。
Path指令解析如下所示:
1.支持的指令:
M = moveto(M X,Y) :相当于 Android Path 里的moveTo(),用于移动起始点
L = lineto(L X,Y): 相当于 android Path 里的lineTo(),用于画线
H = horizontal lineto(H X):画水平线到指定的X坐标位置
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY): 相当于cubicTo(),三次贝塞尔曲线
S = smooth curveto(S X2,Y2,ENDX,ENDY) : 同样三次贝塞尔曲线,更平滑
Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY): quadTo(),二次贝塞尔曲线
T = smooth quadratic Belzier curveto(T ENDX,ENDY): 同样二次贝塞尔曲线,更平滑
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y): 相当于arcTo(),用于画弧
Z = closepath 相当于closeTo(),关闭path
同样的说明,我的那篇博客Android里的SVG也有介绍。
2.使用原则:
- 坐标轴为以(0,0)为中心,X轴水平向右,Y轴水平向下
- 所有指令大小写均可。大写绝对定位,参照全局坐标系;小写相对定位,参照父容器坐标系
- 指令和数据间的空格可以省略
- 同一指令出现多次可以只用一个
SVG的各种转换设计
要从一般使用的PNG图像转换到SVG图像,对于设计师来说,并不是一件难事,因为大部分的设计工具(PS、Illustrator等等)都支持导出各种格式的图像,如PNG、JPG,当然,也包括SVG,因此,设计师可以完全按照原有的方式进行设计,只是最后导出的时候,选择SVG即可。
不要求开发者都去学习使用这些设计工具,开发者可以利用一些工具,自己转换一些比较基础的图像,Android SVG to VectorDrawable 可以在线将普通图像转换为Android Vector Drawable。如图所示:
至于SVG的编辑器,我在Android里的SVG中详细说明了,有兴趣的可以看看。
Android Studio设计
利用Android Studio的Vector Asset,可以非常方便的创建Vector图像,甚至可以直接通过本地的SVG图像来生成Vector图像,系统自带的图片也很丰富。如图所示:
Vector图像
一个Vector图像,同样是一个xml文件,在这里我用一个基本的Vector图像来介绍一下:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="256dp"
android:height="256dp"
android:viewportWidth="32"
android:viewportHeight="32">
<path android:fillColor="#0000ff"
android:pathData="M20.5, 9.5
c-1.955, 0, -3.83, 1.268, -4.5, 3
c-0.63, -1.732, -2.547, -3, -4.5, -3
C8.957, 9.5, 7, 11.432, 7, 14
c0, 3.53, 3.793, 6.257, 9, 11.5
c5.207, -5.242, 9, -7.97, 9, -11.5
C25, 11.432, 23.043, 9.5, 20.5, 9.5z"
/>
</vector>
这个Vector图像的效果是一个心型图案
现在来解释这几个标签:
- android:width :定义图片的宽
- android:height:定义图片的高
- android:viewportHeight 定义图像高被划分的比例大小,例如例子中的32,即把256dp大小的图像划分成32份,后面Path标签中的坐标,就全部使用的是这里划分后的坐标系统。
- android:viewportWidth:定义图像宽被划分的比例大小,效果与viewportHeight一样。这样做有一个非常好的作用,就是将图像大小与图像分离,后面可以随意修改图像大小,而不需要修改PathData中的坐标。
- android:fillColor: 设置绘制的图形中用什么颜色填充
- android:PathData: 这里就用了SVG中的path指令绘制路径。
动态Vector
动态Vector才是Android Vector Drawable的精髓所在
每个Vector动画,基本都包含四部分内容,即:
- Vector:图像资源
- Animated-vector:动画、图像粘合剂
- ObjectAnimator:动画资源
- 代码:启动动画
动态的Vector需要通过animated-vector标签来进行实现,它就像一个粘合剂,将控件与Vector图像粘合在了一起,我们在这里实现一个时钟的小例子:
既然我们要实现时钟的动画,我们首先需要一个时钟的图形,所以在drawable目录下新建一个clock.xml,然后分别绘制分针,时针和表盘。
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportWidth="100"
android:viewportHeight="100">
<!--绘制分针-->
<group
android:name="minutes"
android:pivotX="50"
android:pivotY="50"
android:rotation="0">
<path android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="1.5"
android:pathData="M 50, 50, L 50, 12"/>
</group>
<!-- 绘制时针 -->
<group
android:name="hours"
android:pivotX="50"
android:pivotY="50"
android:rotation="0">
<path android:strokeColor="@android:color/holo_blue_dark"
android:strokeLineCap="round"
android:strokeWidth="1"
android:pathData="M 50, 50, L 24, 50"/>
</group>
<!-- 绘制表盘 -->
<path android:strokeColor="@android:color/holo_red_dark"
android:strokeWidth="1"
android:pathData="@string/path_circle"/>
</vector>
这里出现了几个新的标签和属性,pivotX和pivotY表示缩放的中轴点坐标,距离自身左边缘的位置。我们这里就是指针绕中点旋转。
stroke在我的博客Android里的SVG中有说明。
而path_circle是我在strings.xml中定义的变量,可以绘制出一个圆。
<string name="path_circle">
M 50, 50
m -48, 0
a 48, 48 0 1, 0 96, 0
a 48, 48 0 1, 0 -96, 0
</string>
接着我们还需要为我们的时针和分针设置动画,在animator文件夹下创建hour.xml和minute.xml两个文件。
hour.xml:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="8000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="240"
android:interpolator="@android:anim/linear_interpolator"
/>
minute.xml:
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:repeatCount="7"
android:interpolator="@android:anim/linear_interpolator"
/>
linear_interpolator是线性插值器,在我的博客 Android动画–Interpolator的介绍中详细说明了。
时针代码的含义是在8秒钟从0度旋转到240度。
分针代码的含义是每过一秒旋转一周,旋转8次。
现在就是我们的animated-vector了,anim_clock.xml:
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/clock">
<!-- 用于控制时针的旋转 -->
<target
android:animation="@animator/hour"
android:name="hours"/>
<!-- 用于控制分针的旋转 -->
<target
android:animation="@animator/minute"
android:name="minutes"/>
</animated-vector>
这样就帮我们的时针和分针设置好动画了。
接下来就是测试了,把anim_clock.xml添加到ImageView中。写一个animate()方法:
private void animate() {
Drawable drawable = mImageView.getDrawable();
if (drawable instanceof Animatable) {
((Animatable) drawable).start();
}
}
在mImageView的监听事件中调用animate(),运行。
结束语:本文仅用来学习记录,参考查阅。