2016/04/12(火)追記
23.3.0から、 app:srcCompat
とsetImageResouce()
でしかVectorDrawableが適用されなくなったようです。詳しくはSupport Vector Drawableのバージョン間差異についてに詳しく書かれています。
DroidKaigi公式アプリのアイコンをVectorDrawableにして各解像度ごとのpngファイルをほとんど消すことができたので、対応した内容をまとめておこうと思います。
Suppoort vector drawable #345
下準備
1. Gradleの設定
gradleのbuild toolバージョンをv2.0に上げます。
buildscript {
...
dependencies {
...
classpath 'com.android.tools.build:gradle:2.0.0-beta6'
...
}
}
build.gradleのdefaultConfigに以下を追加します。
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
は明示的に追加しなくても大丈夫です。
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]
(https://medium.com/@chrisbanes/appcompat-v23-2-age-of-the-vectors-91cbafa87c88)
VectorDrawableの作成
Vector Asset Studioなどを使って既存のアイコンと同じVectorDrawableを作っていきます。詳しくは以下を参照してください。
対応箇所
ImageViewのanodroid:src
やTextViewのanodroid:drawableLeft
、Notificationのiconなど、それぞれどう対応していったのかをまとめます。
1. ImageViewのandroid:src
xmlの中のandroid:src
をapp: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を指定すればそのまま動きます。
<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です。
<?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で作られています。