[译] 在 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 的元素内容。

让我们期望这些问题在新的工具上会得到解决。这样这些有潜力的新工具才能够开始达到它们原来的目标。

这篇文章的源代码可以在这里找到。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容