Edited at

VectorDrawable対応まとめ

More than 3 years have passed since last update.


2016/04/12(火)追記

23.3.0から、 app:srcCompatsetImageResouce()でしかVectorDrawableが適用されなくなったようです。詳しくはSupport Vector Drawableのバージョン間差異についてに詳しく書かれています。


DroidKaigi公式アプリのアイコンをVectorDrawableにして各解像度ごとのpngファイルをほとんど消すことができたので、対応した内容をまとめておこうと思います。

Suppoort vector drawable #345


下準備


1. Gradleの設定

gradleのbuild toolバージョンをv2.0に上げます。


build.gradle

buildscript {

...
dependencies {
...
classpath 'com.android.tools.build:gradle:2.0.0-beta6'
...
}
}

build.gradleのdefaultConfigに以下を追加します。


build.gradle

android {

...
defaultConfig {
...
vectorDrawables.useSupportLibrary = true
...
}
}

もしbuild tool v1.5.0以下を使い続けなければならない場合は、以下のように記述します。

android {

defaultConfig {
// Stops the Gradle plugin’s automatic rasterization of vectors
generatedDensities = []
}
// Flag to tell aapt to keep the attribute ids around
aaptOptions {
additionalParameters "--no-version-vectors"
}
}


2. Support Libraryのアップデート

appCompatのバージョンを23.2.0に上げます。

support-vector-drawableは明示的に追加しなくても大丈夫です。


build.gradle

dependencies {

...
compile "com.android.support:appcompat-v7:23.2.0"
compile "com.android.support:support-vector-drawable:23.2.0"
compile "com.android.support:animated-vector-drawable:23.2.0"
...
}

詳しくは、以下の記事が詳しいので参考にしてください。

AppCompat v23.2 — Age of the vectors


VectorDrawableの作成

Vector Asset Studioなどを使って既存のアイコンと同じVectorDrawableを作っていきます。詳しくは以下を参照してください。

既存のアイコンからVectorDrawableを作る


対応箇所

ImageViewのanodroid:srcやTextViewのanodroid:drawableLeft、Notificationのiconなど、それぞれどう対応していったのかをまとめます。


1. ImageViewのandroid:src

xmlの中のandroid:srcapp:srcCompatに変えるだけです。

<ImageView

android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_search"/>

コードで指定する場合は、setImageResourceを使えばOKです。

imageView.setImageResource(R.drawable.ic_search);


2. TextViewのandroid:drawableLeft

TextViewの場合は、直接VectorDrawableを指定すると落ちます。

そのため、StateListDrawableを作成してその中でVectorDrawableを指定してあげる必要があります。

<?xml version="1.0" encoding="utf-8"?>

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_access_time_grey_500_12dp_vector" />
</selector>

<TextView

android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_access_time_grey_500_12dp_vector_state"
android:drawablePadding="@dimen/icon_spacing_small"
android:drawableStart="@drawable/ic_access_time_grey_500_12dp_vector_state"


3. ContextCompat.getDrawable()

何もしなくてOKです。AppCompat23.2.0からは内部のアイコンもVectorDrawableを使っていますし、基本的に~Compatクラスを使っていれば何も対応はいらないはずです。


4. android.support.design.widget.NavigationView

menuの中のandroid:iconにVectorDrawableを指定すればそのまま動きます。


drawer_menu.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">

<group android:checkableBehavior="single">
<item
android:id="@+id/nav_all_sessions"
android:icon="@drawable/ic_event_note_grey_600_24dp_vector"
android:title="@string/all_sessions" />
...
</group>

</menu>



5. Toolbarのメニューアイコン

AppCompatActivityを使っていれば、VectorDrawableを指定するだけでOKです。


menu_sessions.xml

<?xml version="1.0" encoding="utf-8"?>

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<item
android:id="@+id/item_search"
android:icon="@drawable/ic_search_white_24dp_vector"
android:orderInCategory="100"
android:title="@string/search"
app:showAsAction="always" />

</menu>



6. Notificationのアイコン

Lollipop未満はまだ未対応です。

Lollipop以上はdrawable-v21ディレクトリに入れたVectorDrawableを指定するだけでOKです。

Notification notification = new NotificationCompat.Builder(context)

.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_stat_notification_vector)
...


7. GoogleMapのマーカー

BitmapDescriptorFactory.fromResource()を使うと落ちます。

一度Bitmapに変換して、fromBitmap()を使って対応しました。

VectorDrawableをBitmapに変換する


8. Drawableを指定するUIライブラリ

内部でBitmapDrawableにキャストしていたりすると落ちます。

もし対応していない場合は、ライブラリにPullRequestを送って対応しましょう。

DroidKaigiアプリの場合、LikeButtonが対応していなかったのでPullRequestを送りました。

https://github.com/jd-alexander/LikeButton/pull/17


VectorDrawableの属性の説明

主要な属性をまとめておきます。詳しくは公式のVectorDrawableのリファレンスを見てください。

タグ
属性
説明

vector
android:width
表示する横幅。dpで指定します。

vector
android:height
表示する縦幅。dpで指定します。

vector
android:viewportWidth
書き出したVectorの横幅px。px(数値)で指定します。

vector
android:viewportHeight
書き出したVectorの縦幅px。px(数値)で指定します。

vector
android:autoMirrored
RTLで反転させるアイコンの場合、trueを指定します。

path
android:fillColor
pathの色。透過は指定しても無視されます。

path
android:fillAlpha
pathの透過度。0.0(透明)〜1.0を指定します。

path
android:pathData
画像のpath。


対応時に気になりそうなところ

以下対応時の雑感です。


どのくらいapkサイズが減るの?

DroidKaigiアプリの場合、29個のアイコンをVectorDrawableに変えたことで、200KBほど減りました。

Suppoort vector drawable #345

AppCompat v23.2 — Age of the vectorsによると、AppCompat自体も70KB減らせたらしいです。


Allows AppCompat to use vector drawables itself. This itself has shaved off around 70KB from AppCompat’s AAR (~9%).


対応はめんどくさいところもありますが、アイコンの数だけ効果は出るという感じですね。


どういう仕組みなの?

実際にVectorDrawableをどう解釈しているのかはわかりませんが、VectorDrawableCompatが適用されるまでの流れは以下に詳しくまとまっています。

Support LibraryのVectorDrawableCompatが適応されるまでの仕組み


これからはVectorDrawableにすべきなの?

Vectorにすべきなんじゃないでしょうか。

最近のGoogleのOSS plaidも、全てVectorDrawableで作られていますね。

色の変更も楽ですし、採用しない理由は特になさそうです。AndroidStudioのプレビューがまだうまく表示できてない部分がありますが、そのうち対応してくれるはずです。


命名や置き場所はどうするべき?

plaidを真似て、drawableの中に入れることにしました。

ファイル名は、アイコンの場合は ic_xxxx_vector.xmlのようにsuffixに_vectorをつけることにしました。vector以外のアイコンも混在しているため、ぱっと見てVectorDrawableだとわかるようにしたかったからです。

TextViewのdrawableLeftなどで使うのにStateListDrawableを作る必要がある場合は、ic_xxxx_vector_state.xmlのようにしました。ここはどうするべきかまだ決めかねています。


起動時のオーバーヘッドは?

内部を見てないので体感でしかないですが、特に感じませんでした。


ビルド時間に影響はないの?

内部を見てないので体感でしかないですが、特に感じませんでした。


参考資料


1. AppCompat v23.2 — Age of the vectors

基本的なVectorDrawable導入方法がまとまっています。


2. Support LibraryのVectorDrawableCompatが適応されるまでの仕組み

内部でどのようにVectorDrawableCompatが適用されていくかが詳しく説明されています。


3. VectorDrawableリファレンス

VectorDrawableの属性やメソッドが全てまとまっています。


4. Vector Asset Studio

svgファイルからVectorDrawableを作成するAndroidStudioのツールの説明です。


5. plaid

Googleの中の人が作っているOSSです。アイコンは全てVectorDrawableで作られています。