以前は、R.drawable.ic_menu_back といった戻るボタンのリソースが標準であったような気がしたが、見つからない。標準の android のアイコン一覧を探したところ、こちら にそれがあり、リソースは material-design-icons から入手できるようだ。
material-design-icons から入手したリソースを調べるとどうやら 戻るボタンは ic_arrow_back*.png がそれらしい。
find . -name ic_arrow_back_black*png | grep drawable | wc
してみると 40個存在している。(一種類のアイコンのために40個のpngファイルは多すぎでしょ・・・) 軽く調べてみると Lollipop からは VectorDrawable というものが導入されて、それならば、ic_arrow_back_black_24dp.xml 1つで済むらしい。(詳しくは調べていないのであしからず。)
しかし今つくっているアプリは Kitkat 以上を前提としている。 何か回避策もあるようだが、とりあえず戻るボタン1つ必要なだけなので、自前のDrawableをつくって問題を回避することにした。
内容は以下のようになっていて、pathData の部分にSVGの描画コマンドが入っている。 この部分を解析してDrawableクラス内でアイコン描画をし直せばよい。
ic_arrow_back_black_24dp.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
</vector>
内容を把握するために、このXMLからHTML(SVG)を作成して、ブラウザでアイコン描画を確認する。
ic_arrow_back_black.html
<html>
<body>
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 24 24">
<path stroke="black" stroke-width="1" fill="true" d="
M 20,11
H 7.83
l 5.59,-5.59
L 12,4
l -8,8 8,8 1.41,-1.41
L 7.83,13
H 20
v -2
z
"/>
</svg>
</body>
</html>
※)わかりやすいように、pathDataの値に適当にスペースと改行を追加しています。
ic_arrow_back_black.html をブラウザで表示した結果。
あとはこの描画コマンドを android のコードに翻訳すればよい。
※)ちなみに、https://github.com/telly/MrVector 等にあるような PathParser クラスを使えば、今回のように、自分でSVGコマンドをいちいち翻訳する必要はない。
ArrowBackDrawable.java
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.Path;
class ArrowBackDrawable extends ColorDrawable {
ArrowBackDrawable() {
super(Color.BLACK);
}
// 最適化の余地はありますが、説明上わかりやすくなるように実装.
public void draw(Canvas canvas) {
//
// SVGコマンドを翻訳
// M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z
//
// M 20,11
PointF a = new PointF(20f, 11f);
// H 7.83
PointF b = new PointF(7.83f, a.y);
// l 5.59,-5.59
PointF c = new PointF((b.x + 5.59f), (b.y - 5.59f));
// L 12,4
PointF d = new PointF(12f, 4f);
// l -8,8 8,8 1.41,-1.41
PointF e = new PointF((d.x - 8f), (d.y + 8f));
PointF f = new PointF((e.x + 8f), (e.y + 8f));
PointF g = new PointF((f.x + 1.41f), (f.y - 1.41f));
// L 7.83,13
PointF h = new PointF(7.83f, 13f);
// H 20
PointF i = new PointF(20f, h.y);
// v -2
PointF j = new PointF(i.x, (i.y - 2f));
//
// 点を現在のDrawableの大きさに合わせて調整する.
//
PointF[] pointArray = new PointF[]{a, b, c, d, e, f, g, h, i, j};
for (PointF pt : pointArray) {
pt.x = pt.x * (canvas.getWidth() / 24f);
pt.y = pt.y * (canvas.getHeight() / 24f);
}
//
// パスの構築
//
Path path = new Path();
path.moveTo(pointArray[0].x, pointArray[0].y);
for (int index = 1; index < pointArray.length; index++) {
path.lineTo(pointArray[index].x, pointArray[index].y);
}
// z
path.close();
//
// 描画
//
Paint paint = new Paint();
paint.setColor(getColor());
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path, paint);
}
}
アイコンのまわりのマージン設定が足りないような気がするけど、とりあえずこんな感じで。
kotlin に移植した ArrowBackDrawable.kt , 多少の最適化とマージの追加。
ArrowBackDrawable.kt
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.PointF
import android.graphics.drawable.ColorDrawable
import android.graphics.Path
internal class ArrowBackDrawable(color:Int) : ColorDrawable(color) {
val pointArray: Array<PointF>
val paint: Paint
val path: Path
val matrix: Matrix
init {
// M 20,11
val a = PointF(20f, 11f)
// H 7.83
val b = PointF(7.83f, a.y)
// l 5.59,-5.59
val c = PointF(b.x + 5.59f, b.y - 5.59f)
// L 12,4
val d = PointF(12f, 4f)
// l -8,8 8,8 1.41,-1.41
val e = PointF(d.x - 8f, d.y + 8f)
val f = PointF(e.x + 8f, e.y + 8f)
val g = PointF(f.x + 1.41f, f.y - 1.41f)
// L 7.83,13
val h = PointF(7.83f, 13f)
// H 20
val i = PointF(20f, h.y)
// v -2
val j = PointF(i.x, i.y - 2f)
pointArray = arrayOf(a, b, c, d, e, f, g, h, i, j)
paint = Paint()
paint.color = color
paint.style = Paint.Style.FILL
path = Path()
matrix = Matrix()
}
override fun draw(canvas: Canvas) {
//
// パスの構築
//
path.reset()
val firstPtArray = pointArray.take(1)
path.moveTo(firstPtArray[0].x, firstPtArray[0].y)
pointArray.drop(1).forEach { pt->
path.lineTo(pt.x, pt.y)
}
// z
path.close()
//
// パスのトランスフォーム
//
matrix.reset()
val scaleX = (canvas.width*20f/24f) / 24f
val scaleY = (canvas.height*20f/24f) / 24f
val tx = canvas.width*2f/24f
val ty = canvas.height*2f/24f
val values = arrayOf(
scaleX, 0f, tx,
0f, scaleY, ty,
0f, 0f, 1f
).toFloatArray()
matrix.setValues(values)
path.transform(matrix)
//
// 描画
//
canvas.drawPath(path, paint)
}
}