Android自定義動畫三-SVG動畫
本篇文章主要是對SVG的一個介紹和使用,以及Android中對SVG的一個支持,從而可以幫助我們在android下很輕松的通過SVG實現一些非常酷炫的動畫效果。
1.SVG介紹
SVG 是使用 XML 來描述二維圖形和繪圖程序的語言。
它具備以下的特點:
- SVG 指可伸縮矢量圖形 (Scalable Vector Graphics)
- SVG 用來定義用于網絡的基于矢量的圖形
- SVG 使用 XML 格式定義圖形
- SVG 圖像在放大或改變尺寸的情況下其圖形質量不會有所損失
- SVG 是萬維網聯盟的標準
- SVG 與諸如 DOM 和 XSL 之類的 W3C 標準是一個整體
用戶可以直接用代碼來描繪圖像,可以用任何文字處理工具打開SVG圖像,通過改變部分代碼來使圖像具有交互功能,并可以隨時插入到HTML中通過瀏覽器來觀看。
2.SVG 的歷史和優勢
在 2003 年一月,SVG 1.1 被確立為 W3C 標準。
參與定義 SVG 的組織有:太陽微系統、Adobe、蘋果公司、IBM 以及柯達。
與其他圖像格式相比,使用 SVG 的優勢在于:
- SVG 可被非常多的工具讀取和修改(比如記事本)
- SVG 與 JPEG 和 GIF 圖像比起來,尺寸更小,且可壓縮性更強。
- SVG 是可伸縮的
- SVG 圖像可在任何的分辨率下被高質量地打印
- SVG 可在圖像質量不下降的情況下被放大
- SVG 圖像中的文本是可選的,同時也是可搜索的(很適合制作地圖)
- SVG 可以與 Java 技術一起運行
- SVG 是開放的標準
- SVG 文件是純粹的 XML
- SVG 的主要競爭者是 Flash。
與 Flash 相比,SVG 最大的優勢是與其他標準(比如 XSL 和 DOM)相兼容。而 Flash 則是未開源的私有技術。
3.Android使用SVG動畫
從上訴的描述中可以看到SVG是一個可伸縮的矢量圖,而且相較于JPG和GIF圖像尺寸都要更小,并且可以直接在xml中寫入。正式由于這樣的特點,那么 Android 從 5.0 提供了新的API VectorDrawable,通過該對象,我們可以使用矢量圖SVG,在編寫xml文件中,通過關鍵的幾個標簽節點vector,animated-vector,path完成對SVG的編寫以及動畫的實現。
VectorDrawable
可以看到 VectorDrawable 是繼承與Drawable,并且官網說明了是用于創建一個drawable通過XML文件中申明根節點 vector 來實現一個android下的矢量圖。
VectorDrawable 的出現也意味著以前我們放在mdpi, hdpi, xhdpi, xxhdpi中的部分圖片資源(適合用矢量圖描述的,比如圖標)只用一個VectorDrawable 替代就可以了。
在vector節點下我們可以使用 “group、clip-path、path” 來描述一個drawable圖形;“group”節點定義一組path或者子group,而path元素定義需要繪制的路徑。clip-path節點定義當前繪制的剪切路徑。注意,clip-path 只對當前的 group 和子 group 有效。
接下來大家可以通過以下的代碼來學習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目錄下建立了一個 xml 文件,然后指定根節點為 vector 寫出的兩根線段的效果。效果圖如下:
上述代碼中就是定義了一個 group 包含了兩個path,而這兩個 path 分別代表一根直線的繪制。這里要重點去理解的是 path 節點下的 pathData屬性。
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 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):映射前面路徑后的終點
A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧線
Z = closepath():關閉路徑
注釋:以上所有命令均允許小寫字母。大寫表示絕對定位,小寫表示相對定位。
對于做 Android 開發的我們來說要掌握這么多命令,并且如果需要繪制一個比較復雜的圖形那么可能 pathData中需要寫很多的數據,這肯定不是我們想看到的結果,比如下面的這個效果:
對應的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>
對于這么龐大的數據計算我們肯定是不可能去手動畫的,而這張圖片也是我從 FLATICON 網站上隨便找的一張 SVG 圖片,down下來就是現成的 svg 文件了,我們可以直接使用到我們android 中來,所以以后開發中可以讓 UI 給我們這樣的 SVG 文件就行了,當然也有 SVG編輯工具直接產出一些好看的 SVG圖片,都可以在 http://www.flaticon.com中找到。
AnimatedVectorDrawable
AnimatedVectorDrawable 同樣繼承 Drawable,不過它實現了 Animatable 接口,專門用于讓 VectorDrawable 動起來。
首先來看官方的說明:
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 對 VectorDrawable 的某個屬性作動畫。
從API-25開始,AnimatedVectorDrawable 運行在 RenderThread (相反地,早期API是運行在UI線程)。這也就是說 AnimatedVectorDrawable 在UI線程繁忙時也能保持流暢運行。
如果UI線程沒有反應,RenderThread 會持續動畫計算,直到UI線程有能力推進下一幀。因此,沒有辦法準確地同步 RenderThread-enabled 的 AnimatedVectorDrawable 和 UI 線程中的 Animations。此外, Animatable2.AnimationCallback.onAnimationEnd(drawable) 肯定會在 RenderThread 渲染完 AnimatedVectorDrawable 最后一幀時被調用。
Animated vector drawable 可以讓 group 和 path 元素的屬性動態變化。group 定義一組 path 或者子 group,而 path 元素定義需要繪制的路徑。當你想讓 VectorDrawable 呈現動畫效果,在定義 VectorDrawable 的時候需要為 group 和 path 的 android:name 屬性設置一個唯一的名字,以便在Animated vector drawable中找到它們。比如在上面我們畫的兩根直線demo,我現在想讓兩根直線做出以下的動畫效果:
在上面的兩根直線 vector 代碼中是由兩段 path 定義出來的,也分別定義了 name 分別為 path1 path2,所以接下來只需要定義一個 AnimatedVectorDrawable 來分別執行這兩段 path的動畫。
代碼如下:
<?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 節點都是傳入基本動畫(平移,縮放,旋轉,透明度)
此處傳入的為:pathData,效果為讓動畫沿著設定的 valueFrom 和 valueTo 的數據執行
最后要加入節點 android:valueType="pathType",表示數據類型為 path 類型,否則會報錯
在 target 標簽中需要分別為當前你要執行的 path 指定一個 屬性動畫,比如 anim_path1,代碼如下:(必須是屬性動畫,所以在 animator 下創建)
<?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 >
這里還分別給兩個動畫指定了一個動畫插值器:bounce,使合并的時候具有碰撞效果。
輸入框SVG背景動畫
最后再來一個輸入框的背景動畫,效果如下:
結合svg動畫和基本邏輯代碼實現EditText的輸入特效
在 activity 主界面中準備一個 ImageView 和兩個 EditText ,第一個 EditText 沒有背景(后續將為 ImageView 設置圖片來使得 EditText 看起來像有一個背景一樣),第二個 EditText 存在默認背景
這里直接貼出主要的 vector 和 animated_vector 代碼,更詳細的代碼,請到GitHUb下載查看:https://github.com/zphuanlove/AnimationProject
首先是輸入框背景,一根直線和一個對勾的代碼:
<?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"/>
<!--對鉤的處理
大寫的L:后面的數據就是直線的終點
小寫的l:后面的數據是增量-->
<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>
接著就是控制對勾和直線的動畫效果,第一種動畫:直線顯示出來,對勾隱藏:
<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動畫代碼:
<?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動畫代碼:
<?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>
第二種動畫就是直線和對勾隱藏,第三種動畫就是對勾顯示,與第一種動畫都是類似就不在此貼代碼了。
總結
通過本篇文章可以對SVG有一個比較基礎的了解,以及如何在 Android下實現通過 SVG 實現我們的自定義動畫效果!
Thanks!