Android中的矢量動畫

Android中的矢量動畫

自Android 5.0(API 21)開始,Vector drawable(矢量圖像)正式得到了支持,可以通過VectorDrawableAnimatedVectorDrawable來實現矢量圖像。不過這兩個在5.0以上的系統運行,因此爲了支持更多5.0以下的系統,我們可以通過導入Android support library的方式並使用VectorDrawableCompatAnimatedVectorDrawableCompat來實現矢量圖。

對於API 24及更高版本,AnimatedVectorDrawableCompat會自動委託給AnimatedVectorDrawable。 對於API 24之下的版本,此類相當是帶有ObjectAnimatorAnimatorSet屬性的VectorDrawableCompat,從而創建動畫drawable。同理VectorDrawableCompat也會自動委託給VectorDrawable

SVG

SVG的全稱是Scalable Vector Graphics(可縮放矢量圖形),它是專門用於網絡的矢量圖形標準。與SVG對應的是Bitmap(位圖),位圖是由一個個像素點組成的,當圖片放大到一定的大小時,會出現馬賽克現象;而矢量圖則是由一個個點組成,結果數學計算利用直線和曲線繪製而成,無論如何方法都不會出現馬賽克現象。

SVG相比Bitmap:

優點

  • SVG使用XML格式定義圖形,可以很方便的用多種根據打開修改(比如記事本)
  • SVG由點來存儲,由計算機根據點信息繪製,不會失真,無需根據分辨率適配多套圖標
  • SVG的佔用空間明顯比Bitmap小
  • SVG可以轉換爲Path路徑,與Path動畫結合形成豐富的動畫

缺點

  • 沒有位圖表達的色彩豐富

標準的SVG語法中支持很多標籤,比如rect(繪製矩形)、circle(繪製圓形)、line(繪製線段)、polyline(繪製折現)、ellipse(繪製橢圓)、polygon(繪製多邊形)等。

但是Android中並沒有對原生的SVG語法進行支持,而是以一種簡化的方式對SVG進行兼容。Android是通過使用Path標籤實現SVG的圖像。雖然較爲複雜,但是對於SVG圖像的轉換Android Studio中提供了Vector Asset Studio工具,可以輕鬆完成。

VectorDrawable

Android中的矢量圖是VectorDrawable(平時較多使用VectorDrawableCompat以兼容更多版本,API 24及以上自動委託給VectorDrawable),VectorDrawable是一個在XML文件中定義了一組點、線、曲線、顏色和其他相關信息的矢量圖形。其最大的優勢便是縮放不失真,這能有效減少APK體積並減少開發人員的維護,而矢量動畫就是建立在VectorDrawable上形成的。

VectorDrawable以樹狀結構定義屬性,由PathGroup組成。Path用來定義可繪圖的路徑,Group則用於定義一系列路徑或者將Path標籤分組。Path繪製順序與XML文件中顯示的順序相同。它可以使用<vector>元素在XML文件中定義。

在靜態圖像中,單純使用一個path標籤實現還是使用一組path標籤實現沒有什麼實質性的區別;在動畫中,我們可以指定每個path路徑做特定的動畫,通過group標籤則可以將原本由一個path路徑實現的內容分爲多個path路徑來實現,每個path路徑可以指定特定的動畫,這樣一來顯示效果就豐富多彩了。

圖1 層次結構

AnimatedVectorDrawable

AnimatedVectorDrawable將動畫屬性添加到矢量圖形中。我們更多的是使用AnimatedVectorDrawableCompat,其具有更好的兼容性,對於API 24及更高版本,此類會自動委託給AnimatedVectorDrawable

同時自API 25開始,AnimatedVectorDrawable在RenderThread(渲染線程)上運行(而不是如早期API在UI線程上運行),這意味着即使UI線程上有大量負載,動畫也能流暢的展現。

定義一個AnimatedVectorDrawable可以使用三個獨立的XML文件,也可以使用一個XML文件。

AnimatedVectorDrawable can be defined in either three separate XML files, or one XML.

Define an AnimatedVectorDrawable in three separate XML files(三個獨立的XML定義方式)

XML for the VectorDrawable containing properties to be animated(包含動畫屬性的VectorDrawable的XML)

通過對VectorDrawable設置動畫屬性來實現動畫效果。而對於動畫將由ObjectAnimator執行,它的對象可以是根元素,可以是組元素,還可以是路徑元素。(The ObjectAnimator’s target can be the root element, a group element or a path element),對於目標元素,他們要求是不重名的,而沒有動畫的元素則可以不命名。

以下是VectorDrawable的動畫屬性:

元素 動畫屬性 說明
<vector> alpha Drawable的透明度,默認爲1.0(即不透明)
<group> rotation 定義該組圖像的旋轉度數
pivotX 定義該組縮放和旋轉時的X參考點
pivotY 定義該組縮放和旋轉時的Y參考點
scaleX 定義該組X軸縮放大小
scaleY 定義該組Y軸縮放大小
translateX 定義該組沿X軸平移的距離
translateY 定義該組沿Y軸平移的距離
<path> pathData 對SVG矢量圖的描述
fillAlpha 填充顏色的透明度
fillColor 填充顏色
strokeColor 描邊顏色
strokeWidth 描邊寬度
strokeAlpha 描邊透明度
trimPathStart 指定路徑從哪裏開始
trimPathEnd 指定路徑在哪裏結束
trimPathOffset 指定路徑的位移距離
strokeLineCap 描邊線條重點形狀(線帽):butt(無線帽)、round(圓形)、square(方形)

其中,對於pathData有很多指令:

  • M(M x,y)=moveto:將畫筆移動到指定的座標位置
  • L(L x,y)=lineto:畫直線到指定的座標位置
  • H(H x)=horizontal lineto:畫水平線到指定的X座標位置
  • V(V y)=vertical lineto:畫垂直線到指定的Y座標位置
  • C(C x1,y1,x2,y2,endx,endy)=curveto:三階貝塞爾曲線
  • S(S x2,y2,endx,endy)=smooth curveto:根據上一個指令終點最爲起點的三階貝塞爾曲線
  • Q(Q x,y,endx,endy)=quadratic Belzier curve:二階貝塞爾曲線
  • T(T endx,endy)=smooth quadratic Belzier curve:根據上一個指令終點最爲起點的二階貝塞爾曲線
  • A(A rx,ry,xRotation,flag1,flag2,x,y)=elliptical Arc:弧線
    • rx,ry:指橢圓的半軸大小
    • xRotation:指橢圓的X軸和水平方向順時針方向的夾角
    • flag1:只有兩個值,1表示大角度弧度,0表示小角度弧度
    • flag2:只有兩個值,1表示順時針,0表示逆時針
    • x,y:終點座標
  • Z=closepath():關閉路徑

注意:

  • 座標軸以(0,0)點爲中心,X軸水平向右,Y軸水平向下
  • 所有指令大小寫皆可,但是大寫表示絕對定位,參照全局座標系;小寫表示相對定位,參照父容器座標系
  • 指令和數據間的空格可以省略
<vector xmlns:android="http://schemas.android.com/apk/res/android"
     <!--SVG圖形具體大小-->
     android:height="64dp"
     android:width="64dp"
     <!--SVG圖像劃分的比例-->
     android:viewportHeight="600"
     android:viewportWidth="600" >
     <group
         android:name="rotationGroup"
         android:pivotX="300.0"
         android:pivotY="300.0"
         android:rotation="45.0" >
         <path
             android:name="v"
             android:fillColor="#000000"
             android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
     </group>
 </vector>

XML for AnimatedVectorDrawable

組成一個AnimatedVectorDrawable通常包含一個VectorDrawable和一個或多個目標元素。 對於目標元素可以通過android:name指定其目標,並通過android:animation將該目標與其對應的動畫元素(ObjectAnimatorAnimatorSet)進行連接。

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:drawable="@drawable/vectordrawable" >
     <target
         android:name="rotationGroup"
         android:animation="@animator/rotation" />
     <target
         android:name="v"
         android:animation="@animator/path_morph" />
 </animated-vector>

XML for Animations defined using ObjectAnimator or AnimatorSet

這個XML文件是目標元素的動畫效果。例如上面的rotation.xml和path_morph.xml。

rotation.xml通過objectAnimator定義的是在6000毫秒內旋轉360度:

<objectAnimator
     android:duration="6000"
     android:propertyName="rotation"
     android:valueFrom="0"
     android:valueTo="360" />

path_morph.xml則將path從一個形狀變換爲另一個形狀。

對於Path變換必須與之前的形狀兼容,也就是說形狀相同,順序相同,並且路徑中的變化命令也需要相同。

 <set xmlns:android="http://schemas.android.com/apk/res/android">
     <objectAnimator
         android:duration="3000"
         android:propertyName="pathData"
         android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
         android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
         android:valueType="pathType"/>
 </set>

Define an AnimatedVectorDrawable all in one XML file(一個XML搞定AnimatedVectorDrawable)

由於AAPT工具支持將幾個相關XML文件捆綁在一起的新格式,我們可以將之前示例中的XML文件合併到一個XML文件中:

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:aapt="http://schemas.android.com/aapt" >
     <aapt:attr name="android:drawable">
         <vector
             android:height="64dp"
             android:width="64dp"
             android:viewportHeight="600"
             android:viewportWidth="600" >
             <group
                 android:name="rotationGroup"
                 android:pivotX="300.0"
                 android:pivotY="300.0"
                 android:rotation="45.0" >
                 <path
                     android:name="v"
                     android:fillColor="#000000"
                     android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
             </group>
         </vector>
     </aapt:attr>

     <target android:name="rotationGroup">
         <aapt:attr name="android:animation">
             <objectAnimator
             android:duration="6000"
             android:propertyName="rotation"
             android:valueFrom="0"
             android:valueTo="360" />
         </aapt:attr>
     </target>

     <target android:name="v" >
         <aapt:attr name="android:animation">
             <set>
                 <objectAnimator
                     android:duration="3000"
                     android:propertyName="pathData"
                     android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
                     android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
                     android:valueType="pathType"/>
             </set>
         </aapt:attr>
      </target>
 </animated-vector>

兼容性問題

爲了能夠支持在低於Android 5.0(API 21)的設備上繪製矢量圖像和矢量動畫,可以使用VectorDrawableCompatAnimatedVectorDrawableCompat,它們分別通過兩個庫來提供支持:support-vector-drawableanimated-vector-drawable(包含在com.android.support:appcompat兼容包中)。

做法:

  1. 引用com.android.support:appcompat-v7:23.4.0及以上的版本

  2. 引入對於VectorDrawable的支持:

    對於Gradle Plugin 2.0以上:

    android {
      defaultConfig {
        vectorDrawables.useSupportLibrary = true
        }
    }
    

    對於Gradle Plugin 1.5及以下:

    android {
      defaultConfig {
        // Stops the Gradle plugin’s automatic rasterization of vectors
        generatedDensities = []
      }
      // Flag notifies aapt to keep the attribute IDs around
      aaptOptions {
        additionalParameters "--no-version-vectors"
      }
    }
    

兼容包對以下內容是不支持的:

  • Path Morphing:路徑變化動畫
  • Path Interpolation:路徑差值器(只能使用系統差值器)

使用方式

app:srcCompat

對於ImageViewImageButtonFloatingActionButton等可以使用app:srcCompat來指定矢量動畫,並在使用到這個ImageView等的地方獲取drawable,然後start。

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_add" />
(imageview as AnimatedVectorDrawableCompat).Drawable.start()

setImageResource()

還可以通過setImageResource()的方式動態設置。

animatedVectorDrawableCompat = AnimatedVectorDrawableCompat.create(context!!, R.drawable.animated_splash_logo)

imageView.apply {
    setImageDrawable(animatedVectorDrawableCompat)
}

animatedVectorDrawableCompat?.start()

Button、RadioButton

Button並不能直接通過app:srcCompat屬性來使用,需要通過selector標籤來使用。同時在使用時需要將下面的代碼放在Activity的前面。

static {
    AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}

由於在23.2.0中的兼容包中存在問題,23.4.0修復了問題,但爲了區分舊版本其添加了一個標籤,這個標籤需要我們手動打開,而這裏的static就是爲了打開該標籤。

參考

  1. Vector drawables overview
  2. Add multi-density vector graphics
  3. Android自定義控件開發入門與實戰 啓艦著
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章