Android自定義動畫三-SVG動畫

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!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容