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

之前對矢量圖形有所耳聞,但因為Android對于矢量圖形的原生支持較晚,所以一直沒好好研究過(16年2月25就出來的東西,其實就是懶 =。=)。最近工作上正好遇到一個產品需求,想用SVG來解決,借此機會對SVG及Android對于矢量圖形的支持做了次了解,當然最后依然被SVG坑到,變成手寫XML來解決需求,不過這都是題外話了。

SVG是什么?

Scalable Vector Graphics(可縮放矢量圖形)是基于XML,用于描述二維矢量圖形的圖形格式。SVG由W3C制定,是一個開放標準。SVG本身允許包含3種圖形對象類型:

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

以下是一個簡單的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是通過標簽的形式來描述圖形,比如<rect>矩形、<circle>圓形、<polygon>多邊形等等普通標簽,以及支持復雜的路徑的標簽<path>.其中Path標簽可以通俗的理解為是用命令的方式來控制畫筆,比如:
** 將畫筆移動到指定坐標位置 -> 畫一條直線到指定坐標位置 -> 再畫一條曲線 -> 完成后抬起畫筆結束**

當然Path標簽也需要對應的指令來完成操作,比如:
M150 0 L75 200 L225 200 Z

下面是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 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():關閉路徑
注意:以上所有命令均允許小寫字母。大寫表示絕對定位,小寫表示相對定位。

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

Android中的矢量圖替身Vector

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

<!-- 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版本開始,包含了一個Vector Asset Studio的工具,可以選擇導入SVG或者PSD文件來幫助我們生成對應的vector xml文件。
工具本身支持大多數主體元素,但并不包含所有的SVG或PSD特性。SVG導入后的vector xml格式主體參考的是SVG中的pathdata語法,但虛線等屬性、科學計數法、圖案填充(pattern)、漸變(gradient)等特定標簽是不支持的。當然Vector Asset Studio在我們導入的時候會有明確的提示哪些是被支持的、哪些是不被支持的。

以下兩種方法都可以打開Vector Asset Studio:
1. 對應的drawable目錄上右鍵 -> New -> Vector Asset



2. File -> New -> Vector Asset


Android項目中如何使用Vector?

從Android 5.0開始,系統(tǒng)提供了兩個Api來完成對Vector的支持:

  1. VectorDrawable
  2. AnimatedVectorDrawable
    VectorDrawable負責矢量圖形的顯示,而AnimatedVectorDrawable負責基于矢量圖形的動畫效果。

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

只需要引入對應的AppCompat庫

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?

在布局文件中,我們可以針對ImageVIew、ImageButton等控制設置對應的矢量圖片。

如果用的Support包,那只需要通過app:srcCompat=“”屬性來替代以前的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=“”來完成對應的展現。

如何使用AnimatedVectorDrawable?

AnimatedVectorDrawable其實是針對Vector里面的各種元素,提供了屬性動畫的支持方式,從而完成對應的動畫效果。

借用官方文檔里的例子,如果采用多個xml來實現:
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>

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

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

上面是多xml的例子,相當于矢量圖形、動畫目標、動畫規(guī)則是分開的。還有一種方式是用單一的xml文件來描述整個矢量動畫資源,需要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>

因為AnimatedVectorDrawable改變的是vector中各元素的屬性值,所以極大的增添了動畫的實現手段及效果。

不過坑依然有,當API < 21時,擴展包中的AnimatedVectorDrawableCompat會有一些限制:

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路徑動畫基本就可以忽略了。至少在發(fā)此文章的時候以上限制還在,只能默默期待以后吧。

總結

此文只是初略的講述了Android中矢量圖形的基礎使用方法,當然深入來講其實還有很多東西,包括第三方的一些SVG庫、矢量動畫的一些高級用法等等。在此只是做一個用法的基本記錄,同時希望也是借此機會拋磚引玉,給大家提供一個新的處理思路吧。

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

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

推薦閱讀更多精彩內容