AndroidアプリのLauncher IconにVectorDrawableを使用する

VectorDrawableを使うと、端末の解像度というかdensityごとに画像を用意する必要がなくなり、アプリサイズを削減できるし、拡大縮小しても劣化しないためいいことづくめです。ベクトルデータで表現しやすいアイコンにはVectorDrawableを積極的に使うべきでしょう。
Android 4系のサポートがまだ必要な場合でも、Support Libraryを使えば問題なく使えるので、私もどんどん置き換えていきました。
pngで残ったのは、Android 4系でRemoteViewsに使用しているアイコンと、Launcher Iconだけ。Android 4系のサポートを切れればRemoteViewsにも遠慮無くVectorDrawableを使うことができます。あともうちょっと･･･

そんなとき、そういえばLauncher Iconだけは長らくpngのままだなと思い、VectorDrawableが使えないのか調べてみました。

## テンプレートで作られるプロジェクトをみてみる

Android Studio 3.1.3にてアプリのプロジェクトを新規作成するとLauncher Icon関係はこんな感じで作られています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155171/ea77556e-f22f-a3eb-3f14-9ecfe9f18d66.png)
Round Iconについては一端無視して、
API Level 26（Android 8）未満ではmipmap-○○dpiに格納されたpng画像がLauncher Iconに使われています。
そして、API Level 26からはAdaptiveIconが使えるので、API Level 26以上は解像度に関係なく「`mipmap-anydpi-v26/ic_launcher.xml`」が使われます。

中身はこう書かれています。

```xml:mipmap-anydpi-v26/ic_launcher.xml
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
    <background android:drawable="@drawable/ic_launcher_background"/>
    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
```

backgroundとforegroundがそれぞれdrawableを参照しておりそれぞれ以下のようになっています。

```xml:drawable/ic_launcher_background.xml
<?xml version="1.0" encoding="utf-8"?>
<vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillColor="#26A69A"
        android:pathData="M0,0h108v108h-108z"/>
    <path
        android:fillColor="#00000000"
        android:pathData="M9,0L9,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8"/>
    <path
        android:fillColor="#00000000"
        android:pathData="M19,0L19,108"
        android:strokeColor="#33FFFFFF"
        android:strokeWidth="0.8"/>
...
</vector>
```

```xml:drawable-v24/ic_launcher_foreground.xml
<vector
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportHeight="108"
    android:viewportWidth="108">
    <path
        android:fillType="evenOdd"
        android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="78.5885"
                android:endY="90.9159"
                android:startX="48.7653"
                android:startY="61.0927"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0"/>
                <item
                    android:color="#00000000"
                    android:offset="1.0"/>
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
        android:strokeColor="#00000000"
        android:strokeWidth="1"
        />
</vector>
```
※foregroundだけ、drawable-v24なのは、中でグラデーションを使っているからですね。グラデーションが使えるようになったのは24(Android 7)からということですね。

テンプレートで作られるプロジェクトではとっくにLauncher IconがVectorDrawableが使われているってことですね。
なんだ普通に使えるんじゃん。

## mipmapとdrawable

さあやってみよう、とはいかず、その前にmipmapリソースとdrawableリソースの違いが立ち塞がります。

Launcher Iconはmipmapリソースになっています。
AdaptiveIconを定義するxmlはmipmapに置けるようになっていますが、mipmapリソースにVectorDrawableを置くことはできません。
VectorDrawableはdrawableに置く必要があります。
その関係で、AdaptiveIconではXMLでdrawableを参照するという周りくどいことをしています。

しかし、Launcher Iconをmipmapに置かなければならないというルールがあるわけではなく、drawableを指定することもできます。
mipmapは4系の頃使われ始めたもので、Launcher Iconは48dp x 48dpで作られますが、実際に表示されるときは72dp x 72dpなど、アイコンサイズより大きなサイズで表示されることが多いため、mipmapにおいた方がよいということになったようですね。

## やってみる

ということで、私が作っている[このアプリ](https://github.com/ohmae/OrientationFaker)で検証してみました。
アイコンは見ての通り、非常にシンプルなもので、図形の組み合わせでできていて、塗りもベタのみ、グラデーションは使っていない、なのでAPI Level 21以上でつかえるVectorDrawableで表現可能です。
![2018-06-24 (2).png](https://qiita-image-store.s3.amazonaws.com/0/155171/74f2fdd9-9131-a366-a887-761bd6e7f45e.png)
IllustratorからSVGに書き出して、Android StudioでVectorDrawableに変換します。

```xml:AndroidManifest.xml
    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
```
アプリのアイコン指定をmipmapからdrawableに書き換えます。

### 結果

|Android 5.0|Android 4.4|
|:-:|:-:|
|![device-2018-06-24-115415.png](https://qiita-image-store.s3.amazonaws.com/0/155171/318e7274-c7b1-819e-2e2f-e1aa1cf30eae.png)|![device-2018-06-24-115359.png](https://qiita-image-store.s3.amazonaws.com/0/155171/907a0d2e-88d1-3159-0f9f-fe40ac3b6123.png)|

まあ、想像通りというか、API Level 21以上であればちゃんとVectorDrawableが表示されました。4.4ではアプリアイコンが表示できずデフォルトアイコンになっています。

API Level 21以上ならVectroDrawableをLauncher Iconに使える！

Android 4系ではpngの方を利用する必要がありますが、AndroidManifestはバージョンごとに差し替えるしくみはなく、ここでR.drawableで指定してしまっているため、pngもmipmapからdrawableに移動させる必要があります。

pngは「drawable-○○dpi」に置きますので、VectorDrawableは「drawable」ではなく、「drawable-anydpi-v21」などに置きます。こうしておけばAPI Level21以上ではpngに優先してVectorDrawableが使用されます。

## まとめ

- Launcher Iconとしてdrawableリソースを指定すれば、API Level 21（Android 5.0）以上でVectorDrawableが使える
  - グラデーションを使いたい場合は、API Level 24（Android 7.0）以上
- 上記バージョン以下の端末向けには、引き続きpngで用意する必要があり、mipmapではなく、drawableに置く必要がある。

Adaptive Icon対応も含めたic_launcherリソースの配置は以下のようにする。

- 下位バージョン向けdensityごとのリソース
  - `drawable-mdpi/ic_launcher.png`
  - `drawable-hdpi/ic_launcher.png`
  - `drawable-xhdpi/ic_launcher.png`
  - `drawable-xxhdpi/ic_launcher.png`
  - `drawable-xxxhdpi/ic_launcher.png`
- API Level 21以上むけVectorDrawableリソース
  - `drawable-anydpi-v21/ic_launcher.xml`
- API Level 26以上むけAdaptiveIconリソース
  - `drawable-anydpi-v26/ic_launcher.xml`

懸念事項としては、pngもdrawable以下に置く必要があるため、VectorDrawable非対応バージョンでの表示品質がきになるところです。
これについてはホームアプリにもよるとは思いますが、mipmapにおかなくても`getDrawableForDensity(int id, int density)`を使用して、現在のdensityより高解像度向けのアイコンを取得して表示していたりするので、それほど心配する必要は無いと思います。