SVG矢量动画机制
Google在Android 5.X中增加了对 SVG 矢量图形的支持, 这对于创建新的高效率动画具有非常重大的意义。首先,来了解一下什么是SVG.
可伸缩矢最图形 (Scalable Vector Graphics)
定义用于网络的基于矢量的图形
使川 XML 格式定义图形图像任放大或改变尺寸的情况下其图形质量不会有所损失
万维网联盟的标准
· 与诸如 DOM 和 XSL 之类的 W3C 标谁是一个整体
SVG在Web上的应用非常广泛,在Android5.X之前的Android版本上,可以通过一些第三方开源库来在Android 中使用 SVG. 而在Android 5.X 之后. Android 中添加了对SVG的<path>标签的支持.从而让开发者可以使用SVG 来创建更加下富的动画效果.
那么,SVG对比传统的 Bitmap,究竞有什么好处呢?
Bitmap(位图)通过在每个像素点上存储色彩信息来表达图像,而SVG是一个绘阁标淮,与Bitmap对比,SVG最大的优点就是放大不会失真。
而且Bitmap 需要为不同分辨率设计多套 图标, 而矢量图则不需要。
2.1 SVG 中 <path> 标签.
用<path>标签创建 SVG,就像用指令的方式来控制一只画笔,例如移动画笔到某一坐标位置,画一条线,画一条曲线,结束.<path>标签所支持的指令有以下儿种。
M = moveto(M X,Y):将画笔移动到指定的坐标位置,但未发生绘制
L = lineto(L X.Y): 両直线到指定的坐标位置
H = horizontal lineto(H X): 画水平线到指定的 X 坐标位置
V = vertical lineto(V Y):画垂直线到指定的Y坐标位置
C = curveto(C XI,YI.X2,Y2,ENDX,ENDY): 三次贝赛曲线
S = smooth curveto(S X2,Y2.ENDX,ENDY): 三次贝赛曲线
Q = quadratic Belzier curve(Q X.Y.ENDX,ENDY): 二次 贝赛曲线
T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路径后的终点
A=clliptical Arc(A RX.RY,XROTATION,FLAGI,FLAG2,X,Y): 弧线
Z=closepath:关闭路径
在使用上面的指令时,需要注意以下儿点:
坐标轴以(0.0)为中心,X轴水平向右,Y轴水平向下.
所有指令大小写均可.大写绝对定位,参照全局坐标系:小写相对定位,参照父容器坐标系.
指令和数据间的空格可以省略.
同一指令出现多次可以只用一个.
2.2 Android View坐标轴知识补充
View的坐标体系是以左上角为坐标原点,向右为X轴正方向,向下为Y轴正方向。
View绘制,主要是通过Android的2D绘图机制来完成,时机是onDraw方法中,其中包括画布Canvas,画笔Paint。
下面给出示例代码。相关API不是介绍的重点,重点是Canvas的save和restore方法,通过save以后可以对画布进行一些放大缩小旋转倾斜等操作,这两个方法一般配套使用,其中save的调用次数可以多于restore。
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
Bitmap bitmap = ImageUtils.drawable2Bitmap(mDrawable);
canvas.drawBitmap(bitmap, getLeft(), getTop(), mPaint);
canvas.save();
// 注意,这里的旋转是指画布的旋转
canvas.rotate(90);
mPaint.setColor(Color.parseColor("#FF4081"));
mPaint.setTextSize(30);
canvas.drawText("测试", 100, -100, mPaint);
canvas.restore();
}
2.3 SVG 常用指令
- L
绘制直线的指令是“L”,代表从当前点绘制直线到给定点。“L"之后的参数是一个点坐标,如“L 200 400" 绘制直线。 同时, 还可以使用 “H” 和 “V” 指令来绘制水平, 竖直线,后面的参数是x 坐标 (H指令) 或y坐标 (V指令).
- M
M指令类似Android绘图中path 类的 moveTo 方法,即代表将画笔移动到某一点,但并不发生绘制动作. 有关moveTo方法的相关知识戳这里
- A
A指令用来绘制一段弧线,且.允许弧线不闭合。可以把A命令绘制的弧线想象成是椭圆的某一段,A指令以下有七个参数。
1)RX,RY指所在椭园的半轴大小.
- XROTATION 指椭圆的 X 轴与水平方向顺时针方向夹角, 可以想象成一个水平的椭圆绕中心点顺时针旋转XROTATION的角度。
3)FLAGI只有两个值,1表示大角度弧线,0为小角度弧线.
4)FLAG2只有两个值,确定从起点至终点的方向,1为顺时针,0为逆时针。
5)X,Y轴为终点坐标。
SVG的指令参数非常复杂,但是在Android中,不需要绘制太多太复杂的 SVG图形!
2.4 SVG编辑器
SVG 参数的写法固定而且复杂,因此完全可以使用程序来实现,所以一般通过 SVG编辑器来编辑 SVG 图形。 网上有很多 SVG 的在线编辑器, 通过可视化编辑好图形后, 点击 ViewSource 就可以转换为 SVG 代码!
下载离线的 SVG 编辑器,可以获得更为强大的编辑功能,例如常用的 Inkscape, 就是一个非常优秀的离线SVG编辑器.Inkscape win7 64bit download
2.5 Android 中使用SVG
Google 在Android 5.X 中提供了下面两个新的API来帮助支持SVG:
VectorDrawable
AnimatedVectorDrawable
其中,VectorDrawable 可以用来创建基于 XML 的 SVG 图形, 并结合Animated VectorDrawable 来实现动画效果.
2.5.1 VectorDrawable
在XML中创建一个静态的SVG图形, 通常会形成如图1 所示的树形结构。
path是SVG树形结构中的最小单位, 而通过Group 可以将不同的path进行组合。
图1 SVG树形结构
2.5.1.2 创建 SVG 图形
首先需要在 drawable 目录下新建一个 vectordrawable.xml 文件 在该xml文件中通过 ****<vector>标签来声明对SVG的使用,代码如下所示。
vectordrawable.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="200dp"
android:height="200dp"
android:viewportWidth="100"
android:viewportHeight="100"
tools:ignore="ExtraText">
</vector>
其中包含两组宽高属性,height,width 和 viewportHeight, viewportWidth.
这两组属性分别具有不同的含义,height,width表示该SVG图形的具体大小,
而viewportHeight,viewportWidth表示SVG图形划分的比例.
后面在绘制 path 时所使用的参数, 就是根据这两个值来进行转换的,比如上面的代码, 将200dp 划分为100份,如果在绘制图形时使用坐标(50.50), 则意味着该坐标位于该SVG图形正中间。
因此,height,width 的比例与viewportHeight,viewportWidth的比例, 要保持一致, 不然图形就会发生压缩, 形变,
2.5.1.3 给 <vector> 标签增加显示path
还是原来的vectordrawable.xml文件,在原来的基础上增加了 <group> 和 <path> 标签,代码如下
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="200dp"
android:height="200dp"
android:viewportWidth="100"
android:viewportHeight="100"
tools:ignore="ExtraText">
<group
android:name="test"
android:rotation="0">
<path
android:fillColor="#5A8DDF"
android:pathData="M 25 50 a 25,25 0 1,0 50,0"
android:strokeWidth="2"
tools:ignore="VectorRaster"/>
</group>
</vector>
其中 pathData 就是绘制 SVG图形所用到的指令。
在这个例子中, 先使用 “M” 指令, 将画笔移动到 (25,50) 这个坐标, 再通过A指令来绘制一个圆弧并填充。
由于A命令的使用非常广泛而目功能强大,所以这里需要仔细对照前文中A指令的参数,来掌握该命令.
由于这里使用了 android:fillColor 属性来绘制图形,因此绘制出来的是一个填充的图形
如果要绘制一个非填充的图形,可以使用以下属性
android:strokeColor="#5A8DDF"
android:strokeWidth="2"
这里需要注意的是,android:fillColor 属性如果使用如下方式引入颜色,则会报错(编译环境是 AndroidStudio3.2 + compileSdkVersion 28)
android:fillColor="@color/colorAccent"
绘制效果如图 2 所示
图2 SVG图形——填充
图2 SVG图形——非填充
2.5.2 AnimatedVectorDrawable
AnimatedVectorDrawable 的作用就是给 VectorDrawable 提供动画效果。 Google 的 工程师将AnimatedVectorDrawablc 比喻为一个胶水, 通过 AnimatedVectorDrawable 来连接静态的VectorDrawable和动态的 objecAnimator
下面来看看如何使用AnimatedVcctorDrawable实现SVG 图形的动画效果。
首先, 在 drawable目录下 新建anim_vector.xml文件
在该文件中通过 <animated-vector>标签来声明对 AnimatedVcctorDrawable 的使用, 并指定其作用的 path 或 group.
anim_vector.xml
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:drawable="@drawable/vectordrawable"
tools:ignore="NewApi">
<target
android:name="test"
android:animation="@animator/anim_svg1" />
</animated-vector>
其中 drawable属性 对应的即为静态的vectordrawable.xml
android:drawable="@drawable/vectordrawable"
target中的animaton属性对应的即为objectAnimator
android:animation="@animator/anim_svg1" />
通过** AnimatedVectorDrawable 中 target 的 animation 属性**, 将一个动画作用到了对应name
的元素上, objectAnimator 代码如 下
在 animator 目录下新建 anim_svg1.xml文件 代码如 下
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:interpolator="@android:anim/bounce_interpolator"
android:propertyName="pathData"
android:valueFrom="
M 20,80
L 50,80 80,80"
android:valueTo="
M 20,80
L 50,50 80,80"
android:valueType="pathType"/>
最后可以看到, 对动画效果的实现, 还是通过属性动画来实现的, 只是属性稍有不同。
在<group>标签和<path>标签中添加了 rotation,fillColor, pathData 等属性,
那么在objectAnimator中,就可以通过指定android:propertyName="XXXX"属性来选择控制哪一种属性,
通过 android:valueFrom="XXX"和Iandroid:valueTo="XXX"属性,可以控制动画的起始值
唯一需要注意的是,如果指定属性为pathData, 那么需要添加一个属性——android:valueType="path Type"来告诉系统进行 pathData 变换。 类似的情况, 可以使用 rotation 进行旋转动画, 使用 fillColor实现颜色动画, 使用 pathData 进行形状, 位置变化.
2.5.3 使用SVG
当所有的XML文件准备好了以后,就可以在代码中控制SVG 动画,可以非常方便地将一个 Animated VectorDrawable.xml 文件设置给一个 ImageView 作为背景显示.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/anim_vector" />
</RelativeLayout>
需要注意的是,AnimatedVectorDrawable 中指定的 target 的 name 属性,必须与VectorDrawable 中需要作用的 name 属性保持一致,这样系统才能找到要实现动画的元素
即anim_vector.xml 和 vectordrawable.xml中的name属性必须保持一致
2.6 SVG动画 三球轨迹Demo
案例效果
2.6.1 首先创建一个 vector.xml
在drawable目录下新建一个 vector.xml 文件,它是一个 VectorDrawable 完整代码如下:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="200dp"
android:viewportHeight="100"
android:viewportWidth="100">
<group
android:name="sun"
android:pivotX="60"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_sun"
android:fillColor="#18b4ed"
android:pathData="
M 50,50
a 10,10 0 1,0 20,0
a 10,10 0 1,0 -20,0" />
<group
android:name="earth"
android:pivotX="75"
android:pivotY="50"
android:rotation="0">
<path
android:name="path_earth"
android:fillColor="#FFA500"
android:pathData="
M 70,50
a 5,5 0 1,0 10,0
a 5,5 0 1,0 -10,0" />
<group>
<path
android:fillColor="#008000"
android:pathData="
M 90,50
m -5 0
a 4,4 0 1,0 8 0
a 4,4 0 1,0 -8,0" />
</group>
</group>
</group>
</vector>
2.6.2 接着创建2个 objectAnimator
在animator目录下创建2个文件 由于anim_sun.xml 和 anim_earth.xml.它们是objectAnimator动画文件,具体代码如下(由于anim_sun.xml 和 anim_earth.xml的代码相同,所以只贴其中一个,也可以根据需求更改):
anim_sun.xml
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="4000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360" />
2.6.3 创建1个 AnimatedVectorDrawable
在drawable目录下创建一个 sun_system.xml 文件,它是一个AnimatedVectorDrawable,作用是粘合VectorDrawable(vector.xml)和objectAnimator(anim_sun.xml、anim_earth.xml)
具体代码如下所示:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/earth_moon_system">
<target
android:name="sun"
android:animation="@animator/anim_sun" />
<target
android:name="earth"
android:animation="@animator/anim_earth" />
</animated-vector>
注意:target标签中的name属性需要与vector.xml中group标签中的name属性保持一致!
2.6.4 在 activity 中使用SVG
首先创建一个MainActivity,含布局文件activity_main.xml,布局文件中有一个ImageView控件,在控件的src属性中引用上述创建好的sun_system.xml,具体代码如下所示
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/sun_system" />
</RelativeLayout>
除了上述在ImageView控件的src属性中引用之外,还可以通过代码指定的方式引用,具体代码如下:
MainActivity.java
/**
* author : Hashub小晖
* email : hashubng@163.com
* date : 2019/1/5 15:40
* desc : 三球仪轨迹动画
*/
public class SVGSunSystemActivity extends BaseActivity
{
@Bind(R.id.image)
ImageView image;
@Override
protected void initData()
{
// 通过代码引用
image.setImageResource(R.drawable.sun_system);
}
@Override
protected int getLayoutId()
{
return R.layout.activity_svg_test;
}
@OnClick(R.id.image)
public void onViewClicked()
{
// 在ImageView控件的点击事件中开启SVG动画
((Animatable) image.getDrawable()).start();
}
}
运行后就是效果图中的效果!