Android自定義動(dòng)畫三-SVG動(dòng)畫
本篇文章主要是對(duì)SVG的一個(gè)介紹和使用,以及Android中對(duì)SVG的一個(gè)支持,從而可以幫助我們?cè)赼ndroid下很輕松的通過SVG實(shí)現(xiàn)一些非??犰诺膭?dòng)畫效果。
1.SVG介紹
SVG 是使用 XML 來描述二維圖形和繪圖程序的語言。
它具備以下的特點(diǎn):
- SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
- SVG 用來定義用于網(wǎng)絡(luò)的基于矢量的圖形
- SVG 使用 XML 格式定義圖形
- SVG 圖像在放大或改變尺寸的情況下其圖形質(zhì)量不會(huì)有所損失
- SVG 是萬維網(wǎng)聯(lián)盟的標(biāo)準(zhǔn)
- SVG 與諸如 DOM 和 XSL 之類的 W3C 標(biāo)準(zhǔn)是一個(gè)整體
用戶可以直接用代碼來描繪圖像,可以用任何文字處理工具打開SVG圖像,通過改變部分代碼來使圖像具有交互功能,并可以隨時(shí)插入到HTML中通過瀏覽器來觀看。
2.SVG 的歷史和優(yōu)勢(shì)
在 2003 年一月,SVG 1.1 被確立為 W3C 標(biāo)準(zhǔn)。
參與定義 SVG 的組織有:太陽微系統(tǒng)、Adobe、蘋果公司、IBM 以及柯達(dá)。
與其他圖像格式相比,使用 SVG 的優(yōu)勢(shì)在于:
- SVG 可被非常多的工具讀取和修改(比如記事本)
- SVG 與 JPEG 和 GIF 圖像比起來,尺寸更小,且可壓縮性更強(qiáng)。
- SVG 是可伸縮的
- SVG 圖像可在任何的分辨率下被高質(zhì)量地打印
- SVG 可在圖像質(zhì)量不下降的情況下被放大
- SVG 圖像中的文本是可選的,同時(shí)也是可搜索的(很適合制作地圖)
- SVG 可以與 Java 技術(shù)一起運(yùn)行
- SVG 是開放的標(biāo)準(zhǔn)
- SVG 文件是純粹的 XML
- SVG 的主要競爭者是 Flash。
與 Flash 相比,SVG 最大的優(yōu)勢(shì)是與其他標(biāo)準(zhǔn)(比如 XSL 和 DOM)相兼容。而 Flash 則是未開源的私有技術(shù)。
3.Android使用SVG動(dòng)畫
從上訴的描述中可以看到SVG是一個(gè)可伸縮的矢量圖,而且相較于JPG和GIF圖像尺寸都要更小,并且可以直接在xml中寫入。正式由于這樣的特點(diǎn),那么 Android 從 5.0 提供了新的API VectorDrawable,通過該對(duì)象,我們可以使用矢量圖SVG,在編寫xml文件中,通過關(guān)鍵的幾個(gè)標(biāo)簽節(jié)點(diǎn)vector,animated-vector,path完成對(duì)SVG的編寫以及動(dòng)畫的實(shí)現(xiàn)。
VectorDrawable

可以看到 VectorDrawable 是繼承與Drawable,并且官網(wǎng)說明了是用于創(chuàng)建一個(gè)drawable通過XML文件中申明根節(jié)點(diǎn) vector 來實(shí)現(xiàn)一個(gè)android下的矢量圖。
VectorDrawable 的出現(xiàn)也意味著以前我們放在mdpi, hdpi, xhdpi, xxhdpi中的部分圖片資源(適合用矢量圖描述的,比如圖標(biāo))只用一個(gè)VectorDrawable 替代就可以了。
在vector節(jié)點(diǎn)下我們可以使用 “group、clip-path、path” 來描述一個(gè)drawable圖形;“group”節(jié)點(diǎn)定義一組path或者子group,而path元素定義需要繪制的路徑。clip-path節(jié)點(diǎn)定義當(dāng)前繪制的剪切路徑。注意,clip-path 只對(duì)當(dāng)前的 group 和子 group 有效。
接下來大家可以通過以下的代碼來學(xué)習(xí)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">
<!--這里的viewportHeight:100的意思是將高度分為了100份
這里的viewportWidth:100的意思是將寬度分為了100份-->
<group>
<path
android:name="path1"
android:pathData="
M 20,80
L 50,80 80,80"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5"/>
<path
android:name="path2"
android:pathData="
M 20,20
L 50,20 80,20"
android:strokeColor="@android:color/holo_green_dark"
android:strokeLineCap="round"
android:strokeWidth="5"/>
</group>
</vector>
這段代碼就是在Res下的drawable目錄下建立了一個(gè) xml 文件,然后指定根節(jié)點(diǎn)為 vector 寫出的兩根線段的效果。效果圖如下:

上述代碼中就是定義了一個(gè) group 包含了兩個(gè)path,而這兩個(gè) path 分別代表一根直線的繪制。這里要重點(diǎn)去理解的是 path 節(jié)點(diǎn)下的 pathData屬性。
PATH命令
以下這些命令用于路徑數(shù)據(jù):
M = moveto(M X,Y):將畫筆移動(dòng)到指定的坐標(biāo)位置,但未發(fā)生繪制
L = lineto(L X,Y):畫直線到指定的坐標(biāo)位置
H = horizontal lineto(H X):畫水平線到指定的X軸坐標(biāo)
V = vertical lineto(V Y):畫垂直線到指定的Y軸坐標(biāo)
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次貝塞曲線
S = smooth curveto(S X2,Y2,ENDX,ENDY):三次貝塞曲線
Q = quadratic Belzier curveto(Q X,Y,ENDX,ENDY):二次貝塞曲線
T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射前面路徑后的終點(diǎn)
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線
Z = closepath():關(guān)閉路徑
注釋:以上所有命令均允許小寫字母。大寫表示絕對(duì)定位,小寫表示相對(duì)定位。
對(duì)于做 Android 開發(fā)的我們來說要掌握這么多命令,并且如果需要繪制一個(gè)比較復(fù)雜的圖形那么可能 pathData中需要寫很多的數(shù)據(jù),這肯定不是我們想看到的結(jié)果,比如下面的這個(gè)效果:

對(duì)應(yīng)的SVG代碼為:
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 490.358 490.358" style="enable-background:new 0 0 490.358 490.358;" xml:space="preserve">
<g>
<g>
<circle style="fill:#3C92CA;" cx="166.658" cy="164.129" r="137.1"/>
<path d="M490.358,377.629c0-0.3,0-0.6-0.1-0.9c0-0.3-0.1-0.6-0.2-0.9s-0.1-0.6-0.2-0.8c-0.1-0.3-0.2-0.5-0.4-0.8
c-0.1-0.3-0.2-0.5-0.4-0.8c-0.1-0.3-0.3-0.5-0.5-0.7s-0.3-0.5-0.5-0.7s-0.4-0.4-0.7-0.6c-0.2-0.2-0.4-0.4-0.6-0.6
s-0.5-0.3-0.8-0.5c-0.2-0.1-0.4-0.3-0.7-0.4l-110.3-54.4c-0.3-0.2-0.6-0.3-1-0.4c-1.9-0.7-47.4-16.6-84.6,0.1l-82.1,29.7
c-0.2,0.1-0.5,0.2-0.7,0.3c-11.8,5.4-18,17.5-15.5,30.2c2.5,12.6,12.7,21.5,25.5,22.1c0.1,0,0.1,0,0.2,0c0.8,0,4.4-0.2,41.8-2.6
c16.3-1,33.1-2.1,34.6-2.2c4.7-0.2,8.6-4,8.7-8.8c0.1-5-3.8-9.2-8.9-9.3c-0.5,0-0.5,0-35.5,2.2c-17.5,1.1-37.1,2.4-40.4,2.6
c-6.3-0.5-7.8-5.8-8.2-7.4c-0.7-3.4,0.3-7.9,5-10.2l82-29.7c0.2-0.1,0.4-0.2,0.7-0.3c28.8-13.1,66.4-1.3,70.9,0.1l49.1,24.2
l-27,63.7c-13.3-8.2-29.5-10.4-44.5-5.9l-110.2,33.1c-13.1,4-27.6,2.5-39.7-4.1l-172.6-93.5c-6.2-3.4-4.2-9.3-3.7-10.5
c0.6-1.6,3.2-6.6,9.5-4.9l134.1,44.7c4.7,1.6,9.9-1,11.5-5.7c1.6-4.8-1-9.9-5.7-11.5l-134.4-44.9c-0.2-0.1-0.3-0.1-0.5-0.1
c-13.3-3.6-26.2,2.8-31.3,15.6c-5.2,12.9-0.3,26.5,11.9,33.2l172.7,93.8c16.3,8.9,35.8,10.9,53.5,5.5l110.2-33.1
c10.1-3,21-1.6,29.9,4.1l56.2,35.4c0.3,0.2,0.5,0.3,0.8,0.4c0.1,0,0.1,0.1,0.2,0.1h0.1c0.1,0.1,0.2,0.1,0.4,0.1
c0.2,0.1,0.5,0.2,0.7,0.3c0.1,0,0.3,0.1,0.4,0.1c0.2,0.1,0.5,0.1,0.7,0.2c0.1,0,0.3,0,0.4,0.1c0.4,0,0.7,0.1,1.1,0.1l0,0l0,0l0,0
l0,0c0.4,0,0.8,0,1.2-0.1c0.1,0,0.3,0,0.4-0.1c0.3,0,0.5-0.1,0.8-0.2c0.1,0,0.3-0.1,0.4-0.1c0.2-0.1,0.5-0.2,0.7-0.3
c0.1-0.1,0.3-0.1,0.4-0.2c0.2-0.1,0.4-0.2,0.7-0.4c0.1-0.1,0.3-0.2,0.4-0.2c0.2-0.1,0.4-0.3,0.6-0.4c0.1-0.1,0.3-0.2,0.4-0.3
c0.2-0.2,0.3-0.3,0.5-0.5c0.1-0.1,0.2-0.2,0.4-0.4c0.2-0.2,0.3-0.4,0.4-0.6c0.1-0.1,0.2-0.3,0.3-0.4c0,0,0-0.1,0.1-0.1
c0-0.1,0.1-0.2,0.1-0.2c0.1-0.2,0.3-0.5,0.4-0.8l40-85.2c0.1-0.3,0.2-0.5,0.3-0.8c0.1-0.3,0.2-0.6,0.3-0.8
c0.1-0.3,0.1-0.6,0.1-0.9s0.1-0.6,0.1-0.9C490.358,378.229,490.358,377.929,490.358,377.629z M469.358,382.229l-31.9,67.9
l-32.3-20.4l27.7-65.4L469.358,382.229z"/>
<path d="M153.858,173.229h25.4c10.4,0,18.8,8.4,18.8,18.8c0,10.4-8.4,18.8-18.8,18.8h-45.7c-5,0-9.1,4.1-9.1,9.1s4.1,9.1,9.1,9.1
h23.9v17.9c0,5,4.1,9.1,9.1,9.1s9.1-4.1,9.1-9.1v-17.9h3.7c20.4,0,37-16.6,37-37s-16.6-37-37-37h-25.4c-10.4,0-18.8-8.4-18.8-18.8
s8.4-18.8,18.8-18.8h44.9c5,0,9.1-4.1,9.1-9.1s-4.1-9.1-9.1-9.1h-23.1v-17.5c0-5-4.1-9.1-9.1-9.1s-9.1,4.1-9.1,9.1v17.6h-3.7
c-20.4,0-37,16.6-37,37C116.958,156.629,133.558,173.229,153.858,173.229z"/>
<path d="M166.658,310.229c80.6,0,146.1-65.6,146.1-146.1s-65.6-146.2-146.1-146.2s-146.2,65.6-146.2,146.2
S86.058,310.229,166.658,310.229z M166.658,36.129c70.6,0,128,57.4,128,128s-57.4,128-128,128s-128-57.4-128-128
S96.058,36.129,166.658,36.129z"/>
</g>
</g>
</svg>
對(duì)于這么龐大的數(shù)據(jù)計(jì)算我們肯定是不可能去手動(dòng)畫的,而這張圖片也是我從 FLATICON 網(wǎng)站上隨便找的一張 SVG 圖片,down下來就是現(xiàn)成的 svg 文件了,我們可以直接使用到我們android 中來,所以以后開發(fā)中可以讓 UI 給我們這樣的 SVG 文件就行了,當(dāng)然也有 SVG編輯工具直接產(chǎn)出一些好看的 SVG圖片,都可以在 http://www.flaticon.com中找到。
AnimatedVectorDrawable

AnimatedVectorDrawable 同樣繼承 Drawable,不過它實(shí)現(xiàn)了 Animatable 接口,專門用于讓 VectorDrawable 動(dòng)起來。
首先來看官方的說明:
This class animates properties of a VectorDrawable with animations defined using ObjectAnimator or AnimatorSet.
Starting from API 25, AnimatedVectorDrawable runs on RenderThread (as opposed to on UI thread for earlier APIs). This means animations in AnimatedVectorDrawable can remain smooth even when there is heavy workload on the UI thread. Note: If the UI thread is unresponsive, RenderThread may continue animating until the UI thread is capable of pushing another frame. Therefore, it is not possible to precisely coordinate a RenderThread-enabled AnimatedVectorDrawable with UI thread animations. Additionally, onAnimationEnd(Drawable) will be called the frame after the AnimatedVectorDrawable finishes on the RenderThread.
AnimatedVectorDrawable can be defined in either three separate XML files, or one XML.
AnimatedVectorDrawable 通過 ObjectAnimator 或 AnimatorSet 對(duì) VectorDrawable 的某個(gè)屬性作動(dòng)畫。
從API-25開始,AnimatedVectorDrawable 運(yùn)行在 RenderThread (相反地,早期API是運(yùn)行在UI線程)。這也就是說 AnimatedVectorDrawable 在UI線程繁忙時(shí)也能保持流暢運(yùn)行。
如果UI線程沒有反應(yīng),RenderThread 會(huì)持續(xù)動(dòng)畫計(jì)算,直到UI線程有能力推進(jìn)下一幀。因此,沒有辦法準(zhǔn)確地同步 RenderThread-enabled 的 AnimatedVectorDrawable 和 UI 線程中的 Animations。此外, Animatable2.AnimationCallback.onAnimationEnd(drawable) 肯定會(huì)在 RenderThread 渲染完 AnimatedVectorDrawable 最后一幀時(shí)被調(diào)用。
Animated vector drawable 可以讓 group 和 path 元素的屬性動(dòng)態(tài)變化。group 定義一組 path 或者子 group,而 path 元素定義需要繪制的路徑。當(dāng)你想讓 VectorDrawable 呈現(xiàn)動(dòng)畫效果,在定義 VectorDrawable 的時(shí)候需要為 group 和 path 的 android:name 屬性設(shè)置一個(gè)唯一的名字,以便在Animated vector drawable中找到它們。比如在上面我們畫的兩根直線demo,我現(xiàn)在想讓兩根直線做出以下的動(dòng)畫效果:

在上面的兩根直線 vector 代碼中是由兩段 path 定義出來的,也分別定義了 name 分別為 path1 path2,所以接下來只需要定義一個(gè) AnimatedVectorDrawable 來分別執(zhí)行這兩段 path的動(dòng)畫。
代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/two_line">
<target
android:animation="@animator/anim_path1"
android:name="path1"/>
<target
android:animation="@animator/anim_path2"
android:name="path2"/>
</animated-vector>
注意上面:
一般我們使用 propertyName 節(jié)點(diǎn)都是傳入基本動(dòng)畫(平移,縮放,旋轉(zhuǎn),透明度)
此處傳入的為:pathData,效果為讓動(dòng)畫沿著設(shè)定的 valueFrom 和 valueTo 的數(shù)據(jù)執(zhí)行
最后要加入節(jié)點(diǎn) android:valueType="pathType",表示數(shù)據(jù)類型為 path 類型,否則會(huì)報(bào)錯(cuò)
在 target 標(biāo)簽中需要分別為當(dāng)前你要執(zhí)行的 path 指定一個(gè) 屬性動(dòng)畫,比如 anim_path1,代碼如下:(必須是屬性動(dòng)畫,所以在 animator 下創(chuàng)建)
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="pathData"
android:interpolator="@android:interpolator/bounce"
android:valueFrom="
M 20,80
L 50,80 80,80"
android:valueTo="
M 20,80
L 50,50 80,80"
android:valueType="pathType"
>
</objectAnimator >
anim_path2的代碼也是類似:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:propertyName="pathData"
android:interpolator="@android:interpolator/bounce"
android:valueFrom="
M 20,20
L 50,20 80,20"
android:valueTo="
M 20,20
L 50,50 80,20"
android:valueType="pathType"
>
</objectAnimator >
這里還分別給兩個(gè)動(dòng)畫指定了一個(gè)動(dòng)畫插值器:bounce,使合并的時(shí)候具有碰撞效果。
輸入框SVG背景動(dòng)畫
最后再來一個(gè)輸入框的背景動(dòng)畫,效果如下:

結(jié)合svg動(dòng)畫和基本邏輯代碼實(shí)現(xiàn)EditText的輸入特效
在 activity 主界面中準(zhǔn)備一個(gè) ImageView 和兩個(gè) EditText ,第一個(gè) EditText 沒有背景(后續(xù)將為 ImageView 設(shè)置圖片來使得 EditText 看起來像有一個(gè)背景一樣),第二個(gè) EditText 存在默認(rèn)背景
這里直接貼出主要的 vector 和 animated_vector 代碼,更詳細(xì)的代碼,請(qǐng)到GitHUb下載查看:https://github.com/zphuanlove/AnimationProject
首先是輸入框背景,一根直線和一個(gè)對(duì)勾的代碼:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="300dp"
android:height="96dp"
android:viewportHeight="48"
android:viewportWidth="150">
<!-- 繪制底部的直線-->
<path
android:name="bottom"
android:pathData="
M 0,23
L 140,23"
android:strokeAlpha="0.8"
android:strokeColor="@color/colorAccent"
android:strokeLineCap="square"
android:strokeWidth="1"/>
<!--對(duì)鉤的處理
大寫的L:后面的數(shù)據(jù)就是直線的終點(diǎn)
小寫的l:后面的數(shù)據(jù)是增量-->
<path
android:name="right"
android:pathData="
M 128,13
l 3,3
l 5,-6"
android:strokeWidth="2"
android:strokeColor="@color/colorAccent"
android:strokeLineCap="round"/>
</vector>
接著就是控制對(duì)勾和直線的動(dòng)畫效果,第一種動(dòng)畫:直線顯示出來,對(duì)勾隱藏:
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/et_bg">
<target
android:name="bottom"
android:animation="@animator/bottom_line_out" />
<target
android:name="right"
android:animation="@animator/right_default_gone" />
</animated-vector>
bottom_line_out動(dòng)畫代碼:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300"
android:propertyName="trimPathEnd"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
>
</objectAnimator>
right_default_gone動(dòng)畫代碼:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1"
android:valueFrom="1"
android:valueTo="1"
android:propertyName="trimPathStart"
android:valueType="floatType">
</objectAnimator>
第二種動(dòng)畫就是直線和對(duì)勾隱藏,第三種動(dòng)畫就是對(duì)勾顯示,與第一種動(dòng)畫都是類似就不在此貼代碼了。
總結(jié)
通過本篇文章可以對(duì)SVG有一個(gè)比較基礎(chǔ)的了解,以及如何在 Android下實(shí)現(xiàn)通過 SVG 實(shí)現(xiàn)我們的自定義動(dòng)畫效果!
Thanks!