Posted at

【Android】CanvasにPathで破線や点線を書くのに便利なPathEffectの紹介


はじめに

AndroidでCanvasにPathを描画するときに、描画に対して効果を与えることのできるPathEffectの効果についてまとめてみました。

Pathをもとに作成した線に対して一定のルールで効果を与えたいときに便利なものになっています。

特に破線点線なんかを便利に引くことが可能になります。

各PathEffectのサブクラスの効果・挙動は、以下のようなサンプルコードで見ていきます。


private Paint mPaint;
private Path mPath;

private void init() {
// 初期化処理
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(20);

mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
mPath.rewind();
mPath.moveTo(100, 300);
mPath.lineTo(200, 300);
mPath.lineTo(300, 200);
mPath.lineTo(400, 200);

mPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(mPath, mPaint);

mPath.rewind();
mPath.moveTo(500, 100);
mPath.lineTo(550, 100);
mPath.lineTo(700, 200);
mPath.lineTo(550, 300);
mPath.lineTo(500, 300);
mPath.close();

mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(mPath, mPaint);
}

左側の線がPaint.Style.STROKEでの描画、右の図形がPaint.Style.FILLとなっています。


DashPathEffect

DashPathEffectは描写のON/OFFの間隔を指定することで、Pathを破線で描写することができます。

例では20px描画したあと、40pxの空白を入れたものになっています。

Paint.Style.FILLで描画されたものには効果がありません。

mPaint.setPathEffect(new DashPathEffect(new float[]{20, 40}, 0));


PathDashPathEffect

PathDashPathEffectは、任意のPathを等間隔に描写することが可能なPathEffectです。

たとえば、Pathに円形を指定することで点線を表現することが可能です。

指定したPathは、x:0, y:0の位置を基準に効果が適用されるため、作る図形は中心をx:0, y:0になるように作成すると意図通りの動きになると思います。

setStrokeWidthで指定した太さより大きいPathを指定した場合は途切れてしまうので注意してください。

PathDashPathEffect.Styleごとの変化を見ていきます。


PathDashPathEffect.Style.TRANSLATE

PathDashPathEffect.Style.TRANSLATEでは、線の位置に指定したPathの位置を移動したようになります。PathDashPathEffectを使う上で大抵これを選んでおけば問題ないと思います。

例では、20px x 20pxの円を30px間隔で配置しています。(つまり 20-30 = 10pxの空白ができます)

Path shape = new Path(); // 丸型

shape.addOval(-10, -10, 10, 10, Path.Direction.CW);
mPaint.setPathEffect(new PathDashPathEffect(shape, 30, 0, PathDashPathEffect.Style.TRANSLATE));


PathDashPathEffect.Style.ROTATE

PathDashPathEffect.Style.ROTATEでは、線の方向にPathが回転しているような挙動になります。

Path shape = new Path(); // ◆型

shape.moveTo(-10, 0);
shape.lineTo(0, -10);
shape.lineTo(10, 0);
shape.lineTo(0, 10);
shape.close();
mPaint.setPathEffect(new PathDashPathEffect(shape, 30, 0, PathDashPathEffect.Style.ROTATE));


PathDashPathEffect.Style.MORPH

PathDashPathEffect.Style.MORPHでは、線の方向にPathが変形しているようです。

Path shape = new Path(); // ◆型

shape.moveTo(-10, 0);
shape.lineTo(0, -10);
shape.lineTo(10, 0);
shape.lineTo(0, 10);
shape.close();
mPaint.setPathEffect(new PathDashPathEffect(shape, 30, 0, PathDashPathEffect.Style.MORPH));


CornerPathEffect

CornerPathEffect線の角を丸くする効果が加わります。

一つ特徴的なのが、Paint.Style.STROKEでの描画で線の始点と終点の角は丸くならないところです。

例ではradius: 10として作成しています。

mPaint.setPathEffect(new CornerPathEffect(10));


DiscretePathEffect

DiscretePathEffectは線をランダムに崩す効果が加わります。

例の10の部分には崩しの長さ、5の部分には崩すレベルを指定します。

mPaint.setPathEffect(new DiscretePathEffect(10, 5));


SumPathEffect

SumPathEffectは2つのPathEffectを同時に適用することが可能になります。

例えば、2つのPathDashPathEffectのadvancephaseを適切に設定することで、円形と菱形が交互に入る点線を表現することが可能です。

−−・−−・−−のような線を書いて欲しいなどのデザイン要件が来た際に役立つと思います。

Path circleShape = new Path(); // 丸型

circleShape.addOval(-10, -10, 10, 10, Path.Direction.CW);
PathDashPathEffect first = new PathDashPathEffect(circleShape, 80, 0, PathDashPathEffect.Style.TRANSLATE);

Path rectShape = new Path(); // ◆型
rectShape.moveTo(-10, 0);
rectShape.lineTo(0, -10);
rectShape.lineTo(10, 0);
rectShape.lineTo(0, 10);
rectShape.close();
PathDashPathEffect second = new PathDashPathEffect(rectShape, 80, 40, PathDashPathEffect.Style.ROTATE);

mPaint.setPathEffect(new SumPathEffect(first, second));


ComposePathEffect

ComposePathEffectは2つのPathEffectを順番に適用することが可能になります。

outer, innerの2つのPathEffectを用意します。最初にinnerの効果が適用されてから、その適用された図形に対してouterの効果が適用されるような挙動になります。

例では、まず最初にinnerとして指定したPathDashPathEffectの効果が適用されてから、その効果が適用された図形に対してouterとして指定したCornerPathEffectの効果が加わり、菱形の角も含めて丸くなっていることが分かります。

Path shape = new Path(); // ◆型

shape.moveTo(-10, 0);
shape.lineTo(0, -10);
shape.lineTo(10, 0);
shape.lineTo(0, 10);
shape.close();
PathDashPathEffect inner = new PathDashPathEffect(shape, 30, 0, PathDashPathEffect.Style.TRANSLATE);

CornerPathEffect outer = new CornerPathEffect(5);

mPaint.setPathEffect(new ComposePathEffect(outer, inner));