Yo, Yo, 俺らVector xml手書き部

この記事はAndroid 初心者向け Advent Calendar 2019の15日目の記事として登録させていただいています。寝なければまだ今日なのでセーフです。

プロローグ

さて、みなさんはAndroidアプリのリソースになにを使用していますか?pngですか?pngですよね。そうですよねpngですよね。
(webp?知らない子ですね…)
いやまあpngじゃなくてもいいんですけど、Androidでは、画像リソースとしてpngなどのラスタ形式以外にも、ベクター形式の画像を使用することができます。

Androidでのベクターがなんぞや的なアレはアレです、拡大しても画像が荒くなんなくてxmlで表現できるやつです。
詳しくは以下のあたりをば。

このベクター画像、使うとなにが嬉しいかと言うと、 画像サイズの違うだけの同じ画像を複数用意する必要がないのです。
これによってapkサイズも小さくなりハッピー!だったのですが、でかい画像をベクターでやると初回描画に時間がかかる特性に加えて、今はaab使っちゃえばpngとかでも必要なサイズの画像だけよしなにやってくれるのでもうそこまで嬉しいことはなさそう……(要検証)

そこで「Vector xmlを救いたい」と言う思いの元結成されたのが、我々Vector xml手書き部です。

Vector xmlを手書きする

先ほど、ベクター画像を使う利点は同じ画像を複数用意する必要がないこととそこからのapkファイルの縮小と説明しましたが、もう一つ大きな大きな利点が存在します。

このベクター画像、Android Studio上でxmlファイルを編集することで変更を加えることができるのです。

つまり、画像の色を変えたい、縦横比を変更したいなどの場合に、リソースファイルを新たに用意することなくxmlファイルを編集するだけでこれを済ますことができます。
いやまあすごいことっぽく書いてもxmlファイルって時点で当たり前体操なんですが…

Vector画像のxmlファイルの概要

さて、それでは実際にxmlの画像ファイルを見ていきます。

今回のお題は車両通行止めです。 f:id:aosa4054:20191216004343p:plain

<!-- ちゃんと全部今回のために手書きしたよ!! -->
<vector
    android:height="25dp"
    android:viewportHeight="1000"
    android:width="25dp"
    android:viewportWidth="1000"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 背景の白 -->
    <path
        android:fillColor="@android:color/white"
        android:pathData="M0,0 L1000,0 1000,1000 0,1000"/>

    <!-- 赤い円 -->
    <path
        android:fillColor="@color/hyousikiRed"
        android:pathData="M0,500 A500,500 0,1,0 500,0 A500,500 0,0,0 0,500"/>

    <!--白い線-->
    <path
        android:strokeColor="@android:color/white"
        android:strokeWidth="24"
        android:pathData="M48,500 A452,452 0,1,0 500,48 A452,452 0,0,0 48,500"/>

    <!--中の白い円-->
    <path
        android:fillColor="@android:color/white"
        android:pathData="M192,500 A308,308 0,1,0 500,192 A308,308 0,0,0 192,500"/>

    <!--赤い斜線-->
    <path
        android:strokeColor="@color/hyousikiRed"
        android:strokeWidth="192"
        android:pathData="M0,0 L1000,1000"
        android:trimPathStart="0.2"
        android:trimPathEnd="0.8"/>

</vector>

意外と読めますね。 <path />のタグが一本の線とそれに囲まれた図形を表しており、それらを重ねることでパス図形を表現しています。
<path />内の要素はここに使われてる以外にもいろいろあり、それぞれ以下の感じです。

要素 意味
strokeColor 線の色
strokeWidth 線の太さ(px指定)
strokeAlpha 線の透明度
strokeLineCap 線端の形状。enum値で指定。ここでも見てくれ
strokeLineJoin 線結合部の形状。enum値で指定。ここでも見てくれ
strokeMiterLimit 説明が難しいのでここでも見てくれ
fillColor 囲まれた範囲の色
fillAlpha 囲まれた範囲の透明度
fillType 説明が難しいのでここを見てくれ
trimPathStart 始点をパスの途中の位置に設定できる。0~1で指定
trimPathEnd 終点をパスの途中の位置に設定できる。0~1で指定
trimPathOffset トリムする位置をずらせる。0~1で指定

※trim系は特に0.5超えた時の挙動が死ぬほど言葉で説明しづらいので試してみることを推奨します。(正直よくわかってない)
世界一便利な表を作ってしまいましたね。ただのリンク集だけど

勘の良い読者ならもう気づいているでしょうが(言ってみたかった)、これはxmlの皮を被ったsvgです。(名探偵コナン)(筆者はsvgが全くわからない)(野生の馬)

上の表で唯一説明していないpathDataこそがこのパスの本体となっています。
svgの記法で書かれてるのでその記法について詳しくはここを見てくれ!

pathDataの説明

リンク集やってても仕方ないし読んでる人的にもなにが言いたいんじゃだと思うので上の止まれ標識を挙げて実際に説明していきましょう。

まず、簡単な説明として、ここのpathData、または元となったSVGdはいくつかのコマンドにより線(パス)を表し、そこで囲まれた図形(パス図形)とパス自身によってグラフィックを表現します。
とりあえずそれだけ頭に入れてヒャウィゴ!

親要素

まず親要素のコイツについてはなんかまあ見たまんまです。

<vector
    android:height="25dp"
    android:viewportHeight="1000"
    android:width="25dp"
    android:viewportWidth="1000"
    xmlns:android="http://schemas.android.com/apk/res/android">

唯一ちょっと見慣れないviewPortなんとかはpxで表現された画像サイズを表します。

背景の四角
<!-- 背景の白 -->
<path
    android:fillColor="@android:color/white"
    android:pathData="M0,0 L1000,0 1000,1000 0,1000"/>

pathDataで示された範囲内に白を塗ってます。

pathData内では、M0,0で(0,0)の座標に始点を設定、L1000,0 1000,1000 0,1000 で始点から(1000,0), (1000,1000), (0,1000)の順に直線を引いて四角形を表現しています。
ここで使われたコマンドをまとめると、始点を指定するMコマンドと、現在の点から与えられた点へと直線を描くLコマンドです。Lコマンドに複数の座標を与えることで、その順に直線を描いています。

そうあと激アツポイントなんですけど@colorとかが参照できます。

円については今回3つの要素で描かれています。
赤い円、白い線、中の白い円の3つですね。

これらに使われているコマンドは先ほどの始点を指定するM以外ではAだけです。
一番大きな赤い円で見てみましょう。

<!-- 赤い円 -->
<path
    android:fillColor="@color/hyousikiRed"
    android:pathData="M0,500 A500,500 0,1,0 500,0 A500,500 0,0,0 0,500"/>

まず、Mコマンドで(0, 500)に始点を指定しています。
その後にはA500,500 0,1,0 500,0が続きます。

Aは弧を描画するコマンドであり、
これは、A(X方向の半径)(Y方向の半径) (円をどれだけ回転させるか),(描く弧が長弧なら1、短弧なら0のフラグ),(弧の向きの0,1フラグ) (終点座標)で表されます。

このままだと円の一部が切り取られており、綺麗な円になっていないので、この終点から、最初に指定した座標までもう一つAを使って弧を描いています。

赤い斜線
<!--赤い斜線-->
<path
    android:strokeColor="@color/hyousikiRed"
    android:strokeWidth="192"
    android:pathData="M0,0 L1000,1000"
    android:trimPathStart="0.2"
    android:trimPathEnd="0.8"/>

この子はそれほど難しくありませんね!
左上から右下までMLを使って線を引き、二つのtrimPathほげほげで線の始点と終点を調整しています。

まとめ

どうです?意外と書けそうってなりません!?

まとめとか書いときながら特にまとめることもないのですが、まあxmlで画像やると手書きできるメリットがあるよって話でした。
Android Studioxml画像のプレビューが変更から一瞬で反映されるので体験としても悪くはないかと思います。

Android 初心者向け Advent Calendar 2019、明日(今日)は@aosa4054さん、よろしくお願いします。僕なんですけどね。 それではまた明日もお会いしましょう。

参考

なんかSVGのpathをめっちゃ説明してくれてるサイト

ここに限らず、調べながら手書きするならSVGで検索することを強くおすすめします。