Android中矢量圖形的那些事 - SVG or Vector

之前對(duì)矢量圖形有所耳聞,但因?yàn)锳ndroid對(duì)于矢量圖形的原生支持較晚,所以一直沒(méi)好好研究過(guò)(16年2月25就出來(lái)的東西,其實(shí)就是懶 =。=)。最近工作上正好遇到一個(gè)產(chǎn)品需求,想用SVG來(lái)解決,借此機(jī)會(huì)對(duì)SVG及Android對(duì)于矢量圖形的支持做了次了解,當(dāng)然最后依然被SVG坑到,變成手寫(xiě)XML來(lái)解決需求,不過(guò)這都是題外話了。

SVG是什么?

Scalable Vector Graphics(可縮放矢量圖形)是基于XML,用于描述二維矢量圖形的圖形格式。SVG由W3C制定,是一個(gè)開(kāi)放標(biāo)準(zhǔn)。SVG本身允許包含3種圖形對(duì)象類(lèi)型:

  1. 矢量圖形,包括矩形、圓、橢圓、多邊形、直線、任意曲線等。
  2. 嵌入外部圖像,包括png、jpeg、svg等。
  3. 文本。
    [from wikipedia]

以下是一個(gè)簡(jiǎn)單的SVG格式示例:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
  <path d="M150 0 L75 200 L225 200 Z" />
</svg>

SVG是通過(guò)標(biāo)簽的形式來(lái)描述圖形,比如<rect>矩形、<circle>圓形、<polygon>多邊形等等普通標(biāo)簽,以及支持復(fù)雜的路徑的標(biāo)簽<path>.其中Path標(biāo)簽可以通俗的理解為是用命令的方式來(lái)控制畫(huà)筆,比如:
** 將畫(huà)筆移動(dòng)到指定坐標(biāo)位置 -> 畫(huà)一條直線到指定坐標(biāo)位置 -> 再畫(huà)一條曲線 -> 完成后抬起畫(huà)筆結(jié)束**

當(dāng)然Path標(biāo)簽也需要對(duì)應(yīng)的指令來(lái)完成操作,比如:
M150 0 L75 200 L225 200 Z

下面是Path支持的對(duì)應(yīng)指令:

M = moveto(M X,Y) :將畫(huà)筆移動(dòng)到指定的坐標(biāo)位置
L = lineto(L X,Y) :畫(huà)直線到指定的坐標(biāo)位置
H = horizontal lineto(H X):畫(huà)水平線到指定的X坐標(biāo)位置
V = vertical lineto(V Y):畫(huà)垂直線到指定的Y坐標(biāo)位置
C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次貝賽曲線
S = smooth curveto(S X2,Y2,ENDX,ENDY)
Q = quadratic Belzier curve(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():關(guān)閉路徑
注意:以上所有命令均允許小寫(xiě)字母。大寫(xiě)表示絕對(duì)定位,小寫(xiě)表示相對(duì)定位。

當(dāng)然,以上只是SVG中很小的一部分,還有其他很多強(qiáng)大的功能。SVG本身已經(jīng)被廣泛使用,SVG矢量圖與位圖的差別,在于矢量圖可以盡可能的放大而不失真,并且同一張圖像,SVG的實(shí)現(xiàn)可能比png小很多。如果在精簡(jiǎn)安裝包的時(shí)候,可以考慮部分圖標(biāo)改成矢量圖的方式。

Android中的矢量圖替身Vector

Android中對(duì)于矢量圖形的處理,并沒(méi)有選擇直接支持SVG文件,所以云端返回的svg格式圖片是無(wú)法直接被顯示的。
不過(guò)在Android 5.0中提出了矢量圖片的支持,叫做Vector。通過(guò)創(chuàng)建<vector>標(biāo)簽的xml元素來(lái)完成對(duì)矢量圖形的定義。

<!-- res/drawable/heart.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    <!-- intrinsic size of the drawable -->
    android:height="256dp"
    android:width="256dp"
    <!-- size of the virtual canvas -->
    android:viewportWidth="32"
    android:viewportHeight="32">

  <!-- draw a path -->
  <path android:fillColor="#8fff"
      android:pathData="M20.5,9.5
                        c-1.955,0,-3.83,1.268,-4.5,3
                        c-0.67,-1.732,-2.547,-3,-4.5,-3
                        C8.957,9.5,7,11.432,7,14
                        c0,3.53,3.793,6.257,9,11.5
                        c5.207,-5.242,9,-7.97,9,-11.5
                        C25,11.432,23.043,9.5,20.5,9.5z" />
</vector>

Android Studio從1.4版本開(kāi)始,包含了一個(gè)Vector Asset Studio的工具,可以選擇導(dǎo)入SVG或者PSD文件來(lái)幫助我們生成對(duì)應(yīng)的vector xml文件。
工具本身支持大多數(shù)主體元素,但并不包含所有的SVG或PSD特性。SVG導(dǎo)入后的vector xml格式主體參考的是SVG中的pathdata語(yǔ)法,但虛線等屬性、科學(xué)計(jì)數(shù)法、圖案填充(pattern)、漸變(gradient)等特定標(biāo)簽是不支持的。當(dāng)然Vector Asset Studio在我們導(dǎo)入的時(shí)候會(huì)有明確的提示哪些是被支持的、哪些是不被支持的。

以下兩種方法都可以打開(kāi)Vector Asset Studio:
1. 對(duì)應(yīng)的drawable目錄上右鍵 -> New -> Vector Asset



2. File -> New -> Vector Asset


Android項(xiàng)目中如何使用Vector?

從Android 5.0開(kāi)始,系統(tǒng)提供了兩個(gè)Api來(lái)完成對(duì)Vector的支持:

  1. VectorDrawable
  2. AnimatedVectorDrawable
    VectorDrawable負(fù)責(zé)矢量圖形的顯示,而AnimatedVectorDrawable負(fù)責(zé)基于矢量圖形的動(dòng)畫(huà)效果。

與此同時(shí),Android Support Library 23.2.0及之后的版本中,也加入了對(duì)Vector的向下兼容,引入了VectorDrawableCompat和AnimatedVectorDrawableCompat。

只需要引入對(duì)應(yīng)的AppCompat庫(kù)

compile 'com.android.support:appcompat-v7:23.2.0' 

VectorDrawable API 7+
AnimatedVectorDrawable API 11+

另外需要在 build.gradle 文件中新增如下配置:

//For Gradle Plugin 2.0+
 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
    }
 }
//For Gradle Plugin 1.5 or below
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"
  }
}

如何使用VectorDrawable?

在布局文件中,我們可以針對(duì)ImageVIew、ImageButton等控制設(shè)置對(duì)應(yīng)的矢量圖片。

如果用的Support包,那只需要通過(guò)app:srcCompat=“”屬性來(lái)替代以前的android:src即可。
另外記得添加xmlns:app="http://schemas.android.com/apk/res-auto"

<ImageButton
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  app:srcCompat="@drawable/ic_build_black_24dp"
  tools:layout_editor_absoluteX="11dp"
  tools:layout_editor_absoluteY="225dp"
  android:id="@+id/imageButton"
  android:tint="@color/colorAccent" />

如果使用的是Andorid5.0以上的原生支持,可以直接使用android:src=“”來(lái)完成對(duì)應(yīng)的展現(xiàn)。

如何使用AnimatedVectorDrawable?

AnimatedVectorDrawable其實(shí)是針對(duì)Vector里面的各種元素,提供了屬性動(dòng)畫(huà)的支持方式,從而完成對(duì)應(yīng)的動(dòng)畫(huà)效果。

借用官方文檔里的例子,如果采用多個(gè)xml來(lái)實(shí)現(xiàn):
VectorDrawable's XML file: vd.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
   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="vectorPath"
         android:fillColor="#000000"
         android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
   </group>
</vector>

AnimatedVectorDrawable's XML file: avd.xml

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

Animator XML file that is used in the AnimatedVectorDrawable's XML file: rotation.xml

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

最后在布局中的使用方法,跟使用VectorDrawable是一樣的:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/avd"/>

</android.support.constraint.ConstraintLayout>

當(dāng)然到此為止還有最重要的一步,否則看到的還只是靜態(tài)的矢量圖形:

ImageView demoView = (ImageView) findViewById(R.id.imageview);
        if(demoView.getDrawable() instanceof Animatable){
            ((Animatable) demoView.getDrawable()).start();
        }

上面是多xml的例子,相當(dāng)于矢量圖形、動(dòng)畫(huà)目標(biāo)、動(dòng)畫(huà)規(guī)則是分開(kāi)的。還有一種方式是用單一的xml文件來(lái)描述整個(gè)矢量動(dòng)畫(huà)資源,需要Build Tools 版本在24及以上:

<?xml version="1.0" encoding="utf-8"?>
<animated-vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt">
    <aapt:attr name="android:drawable">
        <vector xmlns:android="http://schemas.android.com/apk/res/android"
                android:width="64dp"
                android:height="64dp"
                android:viewportWidth="600"
                android:viewportHeight="600">
            <group
                android:name="rotationGroup"
                android:pivotX="300"
                android:pivotY="300"
                android:rotation="45.0" >
                <path
                    android:name="vectorPath"
                    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:propertyName="rotation"
                android:valueFrom="0"
                android:valueTo="360"
                android:duration="6000"
               />
        </aapt:attr>
    </target>
</animated-vector>

因?yàn)锳nimatedVectorDrawable改變的是vector中各元素的屬性值,所以極大的增添了動(dòng)畫(huà)的實(shí)現(xiàn)手段及效果。

不過(guò)坑依然有,當(dāng)API < 21時(shí),擴(kuò)展包中的AnimatedVectorDrawableCompat會(huì)有一些限制:

Path Morphing (PathType evaluator) Used to morph one path into another path.

Path Interpolation Used to define a flexible interpolator (represented as a path) instead of the system-defined interpolators like LinearInterpolator.

Move along path The geometry object can move around, along an arbitrary path, as part of an animation.

在此限制下,最能發(fā)揮效果的pathdata路徑動(dòng)畫(huà)基本就可以忽略了。至少在發(fā)此文章的時(shí)候以上限制還在,只能默默期待以后吧。

總結(jié)

此文只是初略的講述了Android中矢量圖形的基礎(chǔ)使用方法,當(dāng)然深入來(lái)講其實(shí)還有很多東西,包括第三方的一些SVG庫(kù)、矢量動(dòng)畫(huà)的一些高級(jí)用法等等。在此只是做一個(gè)用法的基本記錄,同時(shí)希望也是借此機(jī)會(huì)拋磚引玉,給大家提供一個(gè)新的處理思路吧。

原文鏈接: Android中矢量圖形的那些事 - SVG or Vector

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,007評(píng)論 25 709
  • 今天我在看某腦SVG視頻和網(wǎng)上查資料時(shí),發(fā)現(xiàn)了和某位大佬的寫(xiě)文章的某種巧合(報(bào)以微妙的笑容)。因?yàn)閺?qiáng)迫癥,所以我想...
    仁昌居士閱讀 5,021評(píng)論 0 11
  • 1. SVG 簡(jiǎn)單介紹 1.1 是什么 SVG是指可伸縮矢量圖形 (Scalable Vector Graphic...
    皇馬船長(zhǎng)閱讀 7,738評(píng)論 0 7
  • Android Vector曲折的兼容之路 兩年前寫(xiě)書(shū)的時(shí)候,就在研究Android L提出的Vector,可研究...
    eclipse_xu閱讀 35,395評(píng)論 30 263
  • 嚴(yán)于利己 寬于待人 內(nèi)圣外王 對(duì)內(nèi)嚴(yán)格 對(duì)外慈悲 悲劇色彩 悲劇美學(xué) 看透生活悲痛苦澀 依舊強(qiáng)作歡顏 煩惱即菩 心...
    角落蜷縮閱讀 251評(píng)論 0 0

友情鏈接更多精彩內(nèi)容