[譯] 在 Android 中使用 VectorDrawable

經常閱讀 Styling Android 的讀者會知道我有多么喜歡用 VectorDrawableAnimatedVectorDrawable 。直到我在寫這篇文章之前我還在等待 VectorDrawableCompat (譯者注:之前大家以為官方會出兼容 Support ,后來官方使用了另外一種方案,詳細內容可戳該鏈接),所以目前矢量圖只能夠在 API 21+ (Lollipop) 上面使用。然而,Android Studio 1.4 增加了對舊 android 的兼容,所以,實際上可以在低于 Lollipop 版本的機器上面使用 VectorDrawable

在使用之前先來快速回顧一下什么是 VectorDrawable 。 本質上來說它其實就是安卓對 SVG path data 的一層封裝。而 SVG paths 是一種以 xml 方式描述復雜圖形元素的東西。(譯者注:感興趣的可以閱讀 W3C 的官方文檔) SVG 很適合用來儲存線條和矢量圖像,但不適合用來儲存攝影圖像。通常在Android中 ShapeDrawable 可以實現一些線條和形狀的繪畫。 但大多數情況我們會將這些矢量圖轉換成不同像素密度位圖來使用。在這篇文章中,我們將會一起來探索如何使用它。

Android Studio 1.4 介紹了如何將SVG圖像導入到 Android Studio 然后再自動轉換為 VectorDrawable 。這些圖標可以來自 material icons pack 或者是單獨的 SVG 文件。導入 material icons 的確可以和 VectorDrawable 配合的天衣無縫,同時 google 也提供了大量的 icon 供我們選擇。然而,導入單獨的 SVG 文件會產生諸多的問題。產生這些問題的主要原因是 VectorDrawable 只支持一部分的 SVG 特性,而像圖像漸變,填充和本地 IRI 引用(能夠給元素一個唯一的索引,然后在 SVG 內通過索引重用)以及圖像的變換等一些我們經常使用的特性都不支持。

舉個例子,即使是如官網 LOGO 這樣簡單的一個SVG圖像(如下圖所示)都不能導入到 Android 中,因為他使用了本地的IRI引用。

現在還不太清楚 Android 去除這些特性是否是出于性能方面原因,(譬如,漸變效果的渲染會比較復雜一點)或者說是為了以后開發的考慮。

如果你足夠了解 SVG 的格式(這個已經不屬于本文的討論范疇了)我們可以手動的修改圖標,然后移除本地 IRI 引用,我們可以對剛才提到的圖標可以使用這個方法。

然而仍然不能夠導入到 Android ,因為會拋出 “premature end of file” 的錯誤信息,并且會指出出現問題的那行。感謝來自Wojtek Kaliciński 的建議,我將 SVG 中 width 和 height 的值從百分比改成絕對值之后就可以導入到 Android 中去了。但是因為水平和豎直移動(Translations)特性不支持,導致所有的元素擺放位置不好。

通過手動將所有的平移(translation)和旋轉(rotation)變換從原來的 SVG 格式轉換到 Android 中所支持的格式后(在<group></group>包含<path></path>來支持變換),我終于能夠將 SVG 圖標導入,并使用 VectorDrawable 將其在 Marshmallow 上正確渲染。

使用 Juraj Novák 所開發的 svg2android 工具可以方便的將 SVG 轉換成 VectorDrawable 。該工具也有限制,那就是不能處理漸變和本地 IRI 引用的問題。但是可以省去我們手動微調的工作還是很不錯的,像我剛才提到的寬高的問題,使用該工具轉換則沒有拋出錯誤。該工具開啟實驗性模式后還支持圖像的變換(Transformation)并且支持的很好。但是對于本地的IRI引用還是需要我們手動的去修改原生的 SVG 文件。

將轉換后的文件放到 res/drawable 文件夾后,我們可以直接當成 drawable 來引用,如下代碼所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingBottom="@dimen/activity_vertical_margin"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  tools:context=".MainActivity">

  <ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:contentDescription="@null"
    android:src="@drawable/svg_logo2" />
</RelativeLayout>

假如我們使用的 gradle plugin 版本是 1.4.0 或者更高的話(截至到我寫這篇文章之前,還未正式發布1.4.0,但是 1.4.0-beta6 已經可以實現這個效果了),那么他將適配到 Android API 1。

那么,適配低版本的原因是什么呢?讓我們來看一下Build文件夾里面所生成的代碼,答案就已經很明顯了。

針對于 API 21 或者更高版本的設備,我們導入的矢量 XML drawable 文件將會被使用,但是對于早起的版本,我們則使用 PNG 代替矢量的 drawable 。

但是如果我們出于 apk 大小的考慮,并不想生成多個 PNG 文件來適配多個分辨率呢?我們可以通過設置 generatedDensities 的值來決定需要生成 PNG 的分辨率和數量。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "com.stylingandroid.vectors4all"
        minSdkVersion 7
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        generatedDensities = ['mdpi', 'hdpi']
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
}

如果我們現在進行 build ,我們會發現它(記住,在 build 之前先清除上一次 build 生成的 PNG 文件)只生成了我們指定的分辨率大小的 PNG 文件。

所以,現在來看一下這些 PNG 圖片里面的內容是什么:

這些圖片本質上跟我之前未修改的 SVG 圖像的內容是相同的。我需要提醒大家,這里會拋出警告信息,告訴我們 <group></group> 元素不支持生成柵格化的圖片格式。但是這并不能消除 VectorDrawable 是 Android 中特有的格式,而且該格式不支持上述特性,這讓我們感到很困惑的事實。

我們現在開始了解為什么圖像的變換特性在導入工具中不被支持了,因為 VectorDrawable 中的 <group></group> 元素不支持導出柵格化的圖片格式,從而導致不能夠向前兼容的問題。這個看上去是一個重大的疏忽:因為在 Lollipop 上面渲染正常的 VectorDrawable 資源,在將他們轉換成 PNG 后則不能夠正確的渲染。

總結:如果使用這些新的工具從 material icons 庫導入資源,那么它們則能夠完美無瑕的渲染。但是,我們需要注意的是它們只具備導入 SVG 的能力以及只支持 SVG 一部分特性的轉換,所以這導致了它們不能夠導入現實世界中大部分的 SVG 文件。此外,對于新工具中將 VectorDrawable 轉換成 PNG 來適配低版本的機器的時候是不支持像素圖轉換的,這讓我們覺得新工具的功能還沒有完成還不能夠拿來使用。

其實這種層次的手動微調花不了太多的時間(譬如修改官方 logo ),尤其我們是先用轉換工具將它們轉換成 VectorDrawable 后。盡管我仍然需要手動的修改圖像變換所涉及到的全部坐標,也就是 SVG pathData 的元素內容。

讓我們期望這些問題在新的工具上會得到解決。這樣這些有潛力的新工具才能夠開始達到它們原來的目標。

這篇文章的源代碼可以在這里找到。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容