背景
位图:像素表示图像 矢量图:数学方程表示图像 维基百科 矢量动画:不断改变矢量图的属性(方程式)形成动画 属性动画(ValueAnimator):在指定的时间内(Time),指定的变化速率下(TimeInterpolator),不断返回指定的(TypeEvaluator)中间值的"中间值生成器"
实现
创建'实现矢量动画需要的三个文件':矢量图、属性动画、助手类
res/drawable/vector.xml
res/animator/object_animator.xml
res/drawable/animated_vector_drawable.xml
Step2 <ImageView android:src="@drawable/animated_vector_drawable" …/> Step3
开始动画
mImageView.getDrawable().start();
注1:助手类指 AnimatedVectorDrawable.java
This class uses ObjectAnimator and AnimatorSet to animate the
properties of a VectorDrawable to create an animated drawable.
注2:Step1中的三个文件
<!--矢量图 res/drawable/vector.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="64"
android:viewportHeight="64">
<group
android:name="group_demo"
android:pivotX="32"
android:pivotY="32"
>
<path
android:name="path_demo"
android:pathData="M0,0 L32,32 L32,0 z"
android:strokeWidth="2"
android:strokeColor="#000000"/>
</group>
</vector>
<!--属性动画 res/animator/object_animator.xml -->
<objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"/>
<!--助手类 res/drawable/animated_vector_drawable.xml -->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
//指定矢量图
android:drawable="@drawable/vector">
<target
//指定将要进行动画的group或path,该名称在vector.xml中定义
android:name="group_demo"
//指定动画类型
android:animation="@animator/object_animator"/>
</animated-vector>
注3:三个文件可合并写到一个文件,方式如下
https://developer.android.google.cn/guide/topics/graphics/vector-drawable-resources
补充一:SVG与VectorDrawable
可伸缩矢量图形(Scalable Vector Graphics),矢量图的一种表述形式,
遵从XML语法,用文本格式的描述性语言来描述图像内容,文件后缀为".svg"
例如:
test.svg
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red" />
</svg>
安卓中使用矢量图,需将"xxx.svg"转换为上文中提到的res/drawable/vector.xml
的格式(即最终的VectorDrawable.java)
SVG图片需要UI提供,也可以手动编写一些简单的vector.xml
补充二:VectorDrawable支持的元素、属性
VectorDrawable并没有支持所有的SVG规范,目前支持的元素、属性如下
<vector>
android:name
//矢量图默认宽高
android:width
android:height
//画布宽高
android:viewportWidth
android:viewportHeight
android:tint
android:tintMode
android:autoMirrored
android:alpha
<group>
android:name
android:rotation
//旋转、缩放一组path时的中心点,相对于画布宽高
android:pivotX
android:pivotY
android:scaleX
android:scaleY
android:translateX
android:translateY
<path>
android:name
android:pathData
android:fillColor
android:strokeColor
android:strokeWidth
android:strokeAlpha
android:fillAlpha
android:trimPathStart
android:trimPathEnd
android:trimPathOffset
android:strokeLineCap
android:strokeLineJoin
android:strokeMiterLimit
android:fillType
<clip-path>
android:name
android:pathData
注1:放大缩小不失真是矢量图特性,矢量图需要有固有宽高的原因:
android/frameworks/base/libs/hwui/VectorDrawable.h中提到
"VectorDrawables are drawn into bitmap caches first"
注2:其余元素、属性的说明可查看
https://developer.android.google.cn/reference/android/graphics/drawable/VectorDrawable
https://www.w3.org/TR/SVG11
补充三:path元素及pathData属性
若把"元素path"看成Android中Path.java的对象(解析vector.xml构建对象时) 那么"属性pathData"中的数据就代表了Path.java中相应的方法(构建对象时依据这些数据调用相应的方法)
例如:
自定义View时,可以通过如下操作画出一个三角形
public class XXX extends View {
@Override
protected void onDraw(Canvas canvas) {
Path path = new Path();
path.moveTo(0, 0);
path.lineTo(32, 32);
path.lineTo(32, 0);
path.close();
canvas.drawPath(path, getPaint());
}
}
vector.xml中pathData配置如下亦代表三角形
<vector >
<path
android:name="path_demo"
android:pathData="M0,0 L32,32 L32,0 z"
/>
</vector>
pathData中数据“对应”Path.java中相应的方法
M对应moveTo()
L对应lineTo()
z对应close()
注1:pathData中M,L,Z...等的语法含义
M:move to 落笔位置
L:line to 划线
Z:close 闭合
A:elliptical arc 圆弧
详情可参阅 https://www.w3.org/TR/SVG11/paths.html
注2:附件VectorDemo有该例
补充四:path元素与Path.java
解析vector.xml时,<path>生成的是VFullPath对象而非Path.java的实例.但二者
最终归宿都是/android/external/skia/src/core/SkPath.cpp
从这点看,补充三中的类比虽不严谨,但不是很过分
vector.xml中Path元素及pathData属性
inflate()时
android/frameworks/base/graphics/java/android/graphics/drawable/VectorDrawable.java
nSetPathString(...)
android/frameworks/base/core/jni/android_graphics_drawable_VectorDrawable.cpp
setPathString(...)
/android/frameworks/base/libs/hwui/VectorDrawable.h
void setData(const Data& data)
draw()时
android/frameworks/base/graphics/java/android/graphics/drawable/VectorDrawable.java
nDraw(...)
android/frameworks/base/core/jni/android_graphics_drawable_VectorDrawable.cpp
draw(...)
android/frameworks/base/libs/hwui/VectorDrawable.cpp
Tree::draw(...)
Tree::drawStaging(...)
Tree::updateBitmapCache(...)
Group::draw(...)
Path::draw(...)
const SkPath& Path::getUpdatedPath() {
VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData());
return mSkPath;
}
Path.java及canvas.drawPath()
构造Path时
android/frameworks/base/graphics/java/android/graphics/Path.java
public Path() {
mNativePath = init1();
}
android/frameworks/base/core/jni/android/graphics/Path.cpp
static jlong init1(JNIEnv* env, jobject clazz) {
return reinterpret_cast<jlong>(new SkPath());
}
canvas.drawPath()时
android/frameworks/base/graphics/java/android/graphics/Canvas.java
public void drawPath(Path,Paint)
android/frameworks/base/core/jni/android_graphics_Canvas.cpp
static void drawPath(...) {
const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawPath(*path, *paint);
}
补充五:参考及Demo
1.https://zh.wikipedia.org/wiki/%E7%9F%A2%E9%87%8F%E5%9B%BE%E5%BD%A2
2.https://zh.wikipedia.org/wiki/%E5%8F%AF%E7%B8%AE%E6%94%BE%E5%90%91%E9%87%8F%E5%9C%96%E5%BD%A2
3.https://www.w3.org/TR/SVG11/paths.html
4.https://developer.android.google.cn/reference/android/graphics/drawable/VectorDrawable
5.https://developer.android.google.cn/guide/topics/graphics/drawable-animation
6.https://developer.android.google.cn/reference/android/graphics/drawable/AnimatedVectorDrawable#OneXML
7.https://developer.android.google.cn/studio/write/vector-asset-studio
8.https://www.androiddesignpatterns.com/2018/11/android-studio-svg-to-vector-cli.html
9.属性动画机制不只是针对view来设计的allows you to animate almost anything,regardless of whether it draws to the screen or not.
https://developer.android.google.cn/guide/topics/graphics/prop-animation
10.文中开头提到的属性动画定义、矢量动画定义源自fyang,维基中没找到相关正式定义,百度中对矢量动画的定义有些"生硬"