背景
位圖:像素表示圖像
矢量圖:數學方程表示圖像 維基百科
矢量動畫:不斷改變矢量圖的屬性(方程式)形成動畫
屬性動畫(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,維基中沒找到相關正式定義,百度中對矢量動畫的定義有些"生硬"