Android 向量圖詳解

sydMobile發表於2019-05-12

官方文件

關於 Vector,在官方開發指南中介紹。本文章是由個人翻譯官方指南然後新增個人理解完成。

由於個人精力有限,多個渠道釋出,排版上可能會有問題,如果影響檢視,請移步 Android 開發者家園

Vector Drawables 概述

VectorDrawable 和 AnimatedVectorDrawable 是在 Android 5.0 系統中第一次加入,當然我們可以使用 Android 的支援庫,來支援舊的版本,通過 VectorDrawableCompat 和 AnimateVectorDrawableCompat 來實現。

VectorDrawble 是在 xml 檔案中定義的向量圖形。xml 檔案中定義的向量圖形,它是一組帶有顏色資訊的點、線和曲線,使用向量圖主要的優點是圖形可伸縮性。可以在不損失顯示質量的情況下進行縮放,這意味著我們可以在不同的螢幕密度的手機上使用相同的檔案。這樣會使 APK 檔案變小更加有利於開發人員維護。我們還可以通過多個 XML 檔案和向量圖結合用於動畫。

既然說起 VectorDrawable 了,那就不得不提 SVG (Scalable Vector Graphic)了,這兩個經常混淆,其實 SVG 就是一種基於可擴充套件語言(xml),用於描述二維向量圖形的一種圖形格式(和我們常見的 .png 等等圖片一樣,都是一種圖片格式),它有自己的一套編寫規範(使用 XML 編寫的),所以這種圖片是根據他自己的一套規範通過 XML 語言編寫而成的圖片。而我們的 VectorDrawable 是程式設計中的,它僅支援 SVG 規範中有限的內容。Android Studio 支援將 SVG 檔案轉換成 VectorDrawable 。這就是他們兩者的關係。

path 中的常用的簡單繪製命令

  • moveto 命令 M 移動到新的位置
  • closepath 命令 Z 封閉路徑,從當前的位置畫一條直線到該路徑或者子路徑起始位置
  • lineto 命令 L ,從當前的位置畫一條線到指定的位置
  • horizontal lineto 命令 H 水平畫一條直線到指定位置
  • vertical lineto 命令 V 垂直畫一條直線到指定位置
  • 貝塞爾曲線 命令 Q
  • 光滑二次貝塞爾曲線 命令 T
  • elliptical arc 命令 A 橢圓弧

每個命令都有大小寫的形式,大寫代表後面的引數是絕對座標,小寫表示相對座標(我們一般用大寫就可以了),引數之間用空格或者逗號隔開。所謂的相對座標是相對於前面一個點的,比如:M30,0 l 10,10 換算成絕對座標就是 M30,0 L40,10

命令使用例項

  • M(x y) 移動到座標 x,y 處
  • Z 後面不接引數,直接連線起點和終點
  • L(x y)直線連線到座標 x,y 處
  • H(x) 水平連線
  • V(y) 垂直連線
  • C(x1 y1 x2 y2 x y)控制點 x1,y1 x2,y2 終點座標 x,y
  • Q(x1 y1 x y)控制帶點 x1,y1 終點座標 x ,y;
  • A(rx,ry,x-axis-rotation,large-arc-flag,sweep-flag,x,y);
    • rx,ry 橢圓半徑
    • x-axis-rotation x 軸旋轉角度
    • large-arc-flag 為 0 的時候表示取小弧度,1 的時候取大弧度
    • sweep-flag 0 取逆時針方向,1 取順時針方向
    • (x,y) 是終點的座標

VectorDrawable 如何定義

VectorDrawable 在 drawable 資料夾中通過 來定義,這裡先來詳細的介紹一下 vector 中的各個屬性(和網上的許多不一樣,網上的大多都沒有自己驗證過)

<!--用於定義 vector drawable-->
<vector
        android:name(用於定義這個 vector drawable 的名字)、
        android:width(定義該 drawble 的內部寬度,支援所有的 Android 系統支援的尺寸單位,通常使用 dp)
        android:height(定義該 drawble 的內部高度,支援所有的 Android 系統支援的尺寸單位,通常使用 dp)
        android:viewportWidth(定義向量圖檢視的寬度,實際上就是對應 path 路徑所使用的資料)
        android:viewportHeight(定義向量圖檢視的高度,實際上就是對應 path 路徑所使用的資料)
        android:tint(定義該 drawble 線條的顏色,定義了後,你再在路徑裡面設定顏色就沒有作用了)
        android:tintMode(定義 tint 顏色的 Porter-Duff blending 模式,預設值為 src_in,暫時不用理會)
        android:autoMirrored 設定當系統為 RTL (right-to-left)佈局的時候,是否自動映象該圖片。
        android:alpha 該圖片的透明屬性
        >
    <grup
          // 有時候我們需要對幾個路徑一起處理,這樣就可以使用 group 元素來把多個 path 放到一起,group 支援的屬性有
          android:name 定義 group 的名字
          android:rotation 定義該 group 的路徑旋轉多少度(順時針旋轉)
          android:pivotX 定義縮放和旋轉該 group 時候的 X 參考點。該值相對於 vector 的 viewport 值來指定的。
          android:pivotY 定義縮放和旋轉該 group 時候的 Y 參考點。該值相對於 vector 的 viewport 值來指定的。
          android:scaleX 定義 X 軸的縮放倍數
          android:scaleY 定義 Y 軸的縮放倍數
          android:translateX 定義移動 X 軸的位移。相對於 vector 的 viewport 值來指定的
          android:translateY 定義移動 Y 軸的位移。相對於 vector 的 viewport 值來指定的>
    	  <path 
                // path 元素裡面的 pathData 就是向量圖的路徑資料,除此之外還可以設定其他的屬性。path 的屬性有:
                android:name 定義該 path 的名字,這樣在其他地方可以通過名字來引用這個路徑
                android:pathData 和 SVG 中 d 元素一樣的路徑資訊
                android:fillColor 定義填充路徑的顏色,如果沒有定義則不填充路徑
                android:strokeWidth 定義路徑邊框的粗細尺寸
                android:strokeAlpha 定義路徑邊框的透明度
                android:fillAlpha 定義填充路徑顏色的透明度
                android:trimPathStart 從路徑起始位置(path 的 M 位置)擷取後剩下的內容,取值範圍從 0 到 1,比如,取值是 0.3 則擷取後的內容就是 原長度 - (原長度*0.3) 
                android:trimPathEnd 從路徑起始位置位置擷取的內容,取值範圍從 0 到 1,比如,取值是 0.3,則擷取後的內容就是 原長度*0.3  
               	需要注意的是如果 trimPathStart 和 strimPathEnd 一塊使用的話,這裡有個規律如果 trimPathEnd 取值大於等於 trimPathStart 的時候結果就是他們兩者分別擷取後內容的交集,如果 trimPathEnd 小於 trimPathStart 的時候,取值就是他們倆的合集(這是經過我無數的實驗得出的規律,你也可以嘗試一下)
                android:trimPathOffset 其實就是設定開始點的偏移位置(取值 從 0 到 1)1 的話就是開始點和結束的互換了,注意這個路徑是可以迴圈的(下面畫圖說明)
                android:strokeLineCap 設定路徑的線頭的形狀,取值為 butt,round,square 預設是 butt
                android:strokeLineJoin 設定路徑交界處的連線方式,取值為 miter、round、bevel 預設是 miter
                android:strokeMiterLimit 設定斜角的上界 預設是 4 (當 strokeLineJoin 設定為 "miter" 的時候,繪製兩條線段以銳角相交的時候,所得的斜面可能相當長,當斜面太長,就會變的不協調。strokeMiterLimit 屬性為斜面的長度設定了一個上限。這個屬性表示斜面長度和線條長度的比值。當 strokeLineJoin 設定為其他屬性時,這個屬性是無效的)
               
        </path>
        
        vector 還支援 clip-path 元素,定義當前繪製的剪下路徑。注意:clip-path 只對當前的 group 和子 group 有效。需要 API 大於等於 21
                
                
                
                
               
    
    
    
    </grup>

</vector>
複製程式碼

offset說明.png

向量動畫

利用 XML 檔案來設定向量動畫

  1. 建立一個向量圖

    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
            android:width="200dp"
            android:height="200dp"
            android:viewportWidth="100"
            android:viewportHeight="100">
        <group android:name="head_eyes">
            <path
                android:fillColor="@color/colorPrimary"
                android:name="head"
                android:pathData=" M50,50 A15,15,0,1,1 50,51Z"
                android:strokeWidth="2"
                android:strokeColor="@color/colorPrimary"/>
            <group android:name="eyes">
                <path android:fillColor="@android:color/white"
                      android:name="eye_left"
                      android:pathData="M55,40A2,2,0,1,1,55,41"/>
                <path android:fillColor="@android:color/white"
                      android:name="eye_right"
                      android:pathData="M69,40A2,2,0,1,1,69,41"/>
            </group>
    
            <path android:fillColor="@color/colorAccent"
                android:pathData="M60,55L55,60H65Z"/>
    
        </group>
    </vector>
    複製程式碼

head.png

哈哈,樣子太醜了,大家就湊合著看把。

  1. 在 drawble 建立向量動畫

    目的就是將我們的向量圖和 objectAnimator 關聯起來

    <?xml version="1.0" encoding="utf-8"?>
    <animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
        // 對應動畫檔案
        android:drawable="@drawable/vector_simple">
        <target
            android:animation="@animator/head"
            // 這裡的名字對應你的向量圖中想要產生動畫的 path 或者 group 或者 clip-path 的名字
            android:name="head_eyes"/>
    
    </animated-vector>
    
    複製程式碼
  2. 在 animator 中建立動畫

    <?xml version="1.0" encoding="utf-8"?>
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <objectAnimator
            android:duration="200"
            android:propertyName="translateY"
            android:repeatCount="infinite"
            android:repeatMode="reverse"
            android:valueFrom="0"
            android:valueTo="20"
            android:valueType="floatType"/>
    </set>
    複製程式碼
  3. 佈局檔案中新增圖片

    <ImageView
            android:id="@+id/iv_android"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/animated_head"
            android:contentDescription="@string/app_name"/>
    複製程式碼
  4. 在程式中啟動動畫

    Drawable drawable =  ivAndroid.getDrawable();
    ((Animatable)drawable).start();
    複製程式碼

效果圖

效果圖.gif

在 objectAnimator 的 propertyName 中有兩個很重要的屬性值,trimPathEndtrimPathStart 表示擷取,和前面介紹的 path 裡面的類似,利用這個可以繪製 vectordrawble。注意如果動畫使用這個兩個屬性值的話,animated-vector 中 target 對應的 name 必須是 path 型別不能是 group 效果圖:

trimPathStart.gif

SVG 圖片轉成 vectordrawble

利用我們 Android Studio 提供的工具就可以將 SVG 格式的圖片直接轉成我們的 vectordrawble 了。

svgtovector.png

選擇圖片.png

歡迎大家關注我的微信公眾號,和我交流分享

相關文章