Drawable とは
Drawable は、Canvas 上で描画可能な抽象概念です。色や画像など、視覚的な要素はすべて Drawable の一種とみなせます。
- XML による定義と、プログラムコードによる動的生成が可能です。
- Android の Drawable は抽象クラスであり、具体的な描画処理はそのサブクラスが担います。
Drawable の利点
- 簡易的に視覚要素を定義でき、自定义 View を作るより開発コストが低い。
- 非画像系の Drawable(ShapeDrawable など)はファイルサイズが小さく、APK の肥大を抑えられます。
内部サイズ(Intrinsic Size)について
- 대부분の Drawable は
getIntrinsicWidth()/getIntrinsicHeight()で内部サイズを取得できます。 - BitmapDrawable では画像そのもののサイズが内部サイズになります。
- ColorDrawable には内部サイズの概念がありません(-1 を返します)。
- 内部サイズと表示サイズは異なります。たとえば View の背景に設定された場合は、View の寸法に合わせて引き伸ばされます。
主要な Drawable クラスとその利用法
BitmapDrawable
画像リソースを表現する Drawable です。
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/your_image"
android:antialias="true"
android:dither="true"
android:filter="true"
android:gravity="center"
android:tileMode="disabled" />
主な属性:
| 属性 | 設定内容 | 備考 |
|---|---|---|
android:src | 画像リソース ID | 必須 |
android:antialias | アンチエイリアスの有無 | 推奨 |
android:dither | ディザリングの有無 | 推奨 |
android:filter | スケーリング時の補間処理 | 推奨 |
android:gravity | 表示位置の指定 | 複数指定可 |
android:tileMode | 平铺モード(repeat/mirror/clamp) | デフォルトは disable |
NinePatchDrawable(.9 画像)
特定の領域だけを伸縮させる画像で、UI 要素のサイズ変更時に画質の劣化を避けられます。
ShapeDrawable
幾何学的な図形を XML で記述する手段です。実体は GradientDrawable です。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<gradient
android:type="linear"
android:startColor="@color/red"
android:endColor="@color/blue"
android:angle="45"/>
<stroke
android:width="2dp"
android:color="@color/stroke_color"
android:dashWidth="4dp"
android:dashGap="2dp"/>
<size
android:width="100dp"
android:height="100dp"/>
</shape>
主な要素:
<corners>:角の丸み設定<gradient>:グラデーション(線形、径向、スキャン)<solid>:単色描画(gradient と併用不可)<stroke>:線(実線/虚線)<size>:内部サイズの明示
LayerDrawable
複数の Drawable を重ねて表示するためのクラスで、XML では <layer-list> を利用します。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#0ac39e"/>
</shape>
</item>
<item android:bottom="6dp">
<shape android:shape="rectangle">
<solid android:color="#ffffff"/>
</shape>
</item>
</layer-list>
StateListDrawable
View の状態(pressed/focused/enabled など)に応じて Drawable を切り替える機能です。XML では <selector> を使います。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/pressed_bg"/>
<item android:state_focused="true"
android:drawable="@color/focused_bg"/>
<item android:drawable="@color/default_bg"/>
</selector>
LevelListDrawable
level(0~10000)に応じて表示する Drawable を切り替えるものです。
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:minLevel="0" android:maxLevel="100"
android:drawable="@drawable/low_battery"/>
<item android:minLevel="101" android:maxLevel="500"
android:drawable="@drawable/mid_battery"/>
<item android:minLevel="501" android:maxLevel="10000"
android:drawable="@drawable/high_battery"/>
</level-list>
TransitionDrawable
2 つの Drawable 間でフェードアニメーションを実行します。
<?xml version="1.0" encoding="utf-8"?>
<transition xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/image1"/>
<item android:drawable="@drawable/image2"/>
</transition>
実行例:
val transition = findViewById(R.id.imageView).drawable as TransitionDrawable
transition.startTransition(300)
ClipDrawable
指定された方向から徐々にオブジェクトを表示/削除できるcovering animation 用の Drawable です。
主なパラメータ:
android:clipOrientation:clip 方向(horizontal/vertical)android:gravity:clip 基準位置
AnimationDrawable
逐次アニメーション(フレーム単位)を実現します。XML は <animation-list> で記述します。
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/frame1" android:duration="100"/>
<item android:drawable="@drawable/frame2" android:duration="100"/>
...
</animation-list>
実行:
val animatable = imageView.drawable as AnimationDrawable
animatable.start()
その他のDrawable
| クラス | XMLタグ | 用途 |
|---|---|---|
| ColorDrawable | <color> | 単色描画 |
| RotateDrawable | <rotate> | 回転表示 |
| RippleDrawable | <ripple> | Touch feedback(API 21+) |
| VectorDrawable | <vector> | SVG形式の静的ベクター画像 |
| AnimatedVectorDrawable | <animated-vector> | ベクター画像のアニメーション |
| AnimatedStateListDrawable | <animated-selector> | 状態変化時のアニメーション |
SVGベースのベクター描画(VectorDrawable)
Android 5.0(API 21)で導入されたベクター画像実装で、解像度に依存せず拡大しても劣化しません。
基本構成
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group>
<path
android:name="arrow"
android:pathData="M12,2 L2,22 L22,22 Z"
android:fillColor="#FF000000"/>
</group>
</vector>
主要な SVG 命令一覧
| コマンド | 説明 |
|---|---|
| M(moveto) | 描画開始位置へ移動 |
| L(lineto) | 直線描画 |
| H/V(horizontal/vertical lineto) | 軸方向への直線 |
| C(curveto) | 3次ベジェ曲線 |
| A(elliptical arc) | 楕円弧描画 |
| Z(closepath) | パスを閉じる |
AnimatedVectorDrawable によるアニメーション
ベクター画像の path データや描画プロパティをアニメーションさせる仕組みです。
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/vector_drawable+xml">
<target
android:name="path1"
android:animation="@animator/trim_path"/>
</animated-vector>
アニメーション定義例(trimPath):
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="trimPathEnd"
android:valueFrom="0"
android:valueTo="1"
android:duration="500"
android:interpolator="@android:interpolator/accelerate_decelerate"/>
カスタム Drawable の実装
独自の視覚要素を実現するには、Drawable クラスを継承し、draw() メソッドをオーバーライドします。
class CustomCircleDrawable(private val color: Int) : Drawable() {
private val paint = Paint().apply {
isAntiAlias = true
this.color = color
}
override fun draw(canvas: Canvas) {
val b = bounds
canvas.drawCircle(
b.exactCenterX(),
b.exactCenterY(),
minOf(b.width(), b.height()) / 2f,
paint
)
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(filter: ColorFilter?) {
paint.colorFilter = filter
invalidateSelf()
}
override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
}
注意点:
getIntrinsicWidth()/getIntrinsicHeight()を実装する必要がある場合(wrap_content 対応時)- 描画内容の変更タイミングを
invalidateSelf()で通知すること
ShapeDrawable の拡張サブクラス
ShapeDrawable を直接利用した描画の他、以下のような具象サブクラスも用意されています:
val rectShape = RectShape()
val rectDrawable = ShapeDrawable(rectShape).apply {
paint.color = Color.RED
paint.style = Paint.Style.FILL
}
val ovalShape = OvalShape()
val ovalDrawable = ShapeDrawable(ovalShape).apply {
paint.color = Color.BLUE
}
val arcShape = ArcShape(startAngle = 45f, sweepAngle = 180f)
val arcDrawable = ShapeDrawable(arcShape).apply {
paint.color = Color.YELLOW
}
また、RoundRectShape を継承した PaintDrawable を使えば、角丸矩形の描画をプログラムからも簡潔に実現できます。