🧑🎨
AndroidでのView描画の基礎
概要
View・Canvas・Paintの基礎を抑える
開発環境
タイトル | 内容 |
---|---|
Android | 10 |
AndroidStudio | Bumblebee |
macOS | 12.3 |
minSdk | 28 |
targetSdk | 31 |
実行端末 | Pixel3a |
参考資料
ビューとは
ビュー(ウィジェット/レイアウト)はViewの派生クラス。
なので、独自のビューを作成したい場合はViewを継承する。
カスタムビューの基本
java
public class SampleView extends View {
private Paint paint;
public SampleView(Context context) {
super(context);
}
public SampleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// スタイル準備
paint = new Paint();
paint.setColor(Color.CYAN);
paint.setStrokeWidth(100);
}
public SampleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.WHITE);
canvas.drawPoint(100.0f, 100.0f, paint);
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.canvastestjava.custom_view.SampleView
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
具体的な描画処理はonDrawに記述する
protected void onDraw(Canvas canvas)
Canvas
- 基本的な描画の種類
- 矩形・円・楕円・角丸矩形・弧・点・線・画像・文字列
Paint
- スタイル情報を管理するためのクラス
- 基本的な指定
- setColor・setWidth
点
java
public class DotView extends View {
private Paint paint;
public DotView(Context context) {
super(context);
}
public DotView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.CYAN);
paint.setStrokeWidth(30);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
// 30づつ座標をずらす
float[] ps = {
50, 100, // 1個目 (x:50, y:100)
80, 130, // 2個目 (x:80, y:130)
110, 160, // 3個目 (x:110, y:160)
140, 190, // 4個目 (x:140, y:190)
};
canvas.drawPoints(ps, paint);
}
}
直線
java
public class LineView extends View {
private Paint buttPaint;
private Paint roundPaint;
private Paint squarePaint;
public LineView(Context context) {
super(context);
}
public LineView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
buttPaint = new Paint();
roundPaint = new Paint();
squarePaint = new Paint();
buttPaint.setColor(Color.CYAN);
roundPaint.setColor(Color.CYAN);
squarePaint.setColor(Color.CYAN);
buttPaint.setStrokeWidth(50);
roundPaint.setStrokeWidth(50);
squarePaint.setStrokeWidth(50);
// Paint.Cap.BUTT 先端処理なし
buttPaint.setStrokeCap(Paint.Cap.BUTT);
// Paint.Cap.ROUND 先端を丸める
roundPaint.setStrokeCap(Paint.Cap.ROUND);
// Paint.Cap.SQUARE 先端を四角形にする
squarePaint.setStrokeCap(Paint.Cap.SQUARE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
float[] buttPositions = {100.0f, 500.0f, 1000.0f, 500.0f};
float[] roundPositions = {100.0f, 700.0f, 1000.0f, 700.0f};
float[] squarePositions = {100.0f, 900.0f, 1000.0f, 900.0f};
canvas.drawLine(
buttPositions[0], // 開始点X座標
buttPositions[1], // 開始点Y座標
buttPositions[2], // 終了点X座標
buttPositions[3], // 終了点Y座標
buttPaint);
canvas.drawLine(
roundPositions[0], // 開始点X座標
roundPositions[1], // 開始点Y座標
roundPositions[2], // 終了点X座標
roundPositions[3], // 終了点Y座標
roundPaint);
canvas.drawLine(
squarePositions[0], // 開始点X座標
squarePositions[1], // 開始点Y座標
squarePositions[2], // 終了点X座標
squarePositions[3], // 終了点Y座標
squarePaint);
}
}
矩形
java
public class RectView extends View {
private Paint fillPaint;
private Paint fillAndStrokePaint;
private Paint strokePaint;
public RectView(Context context) {
super(context);
}
public RectView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
fillPaint = new Paint();
fillAndStrokePaint = new Paint();
strokePaint = new Paint();
fillPaint.setColor(Color.CYAN);
fillAndStrokePaint.setColor(Color.CYAN);
strokePaint.setColor(Color.RED);
fillPaint.setStrokeWidth(20);
fillAndStrokePaint.setStrokeWidth(20);
strokePaint.setStrokeWidth(20);
fillPaint.setStyle(Paint.Style.FILL);
fillAndStrokePaint.setStyle(Paint.Style.FILL_AND_STROKE);
strokePaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
float[] fillPs = {100, 100, 300, 300};
float[] fillAndStrokePs = {400, 100, 600, 300};
float[] strokePs = {700, 100, 900, 300};
canvas.drawRect(fillPs[0], fillPs[1], fillPs[2], fillPs[3], fillPaint);
canvas.drawRect(fillAndStrokePs[0], fillAndStrokePs[1], fillAndStrokePs[2], fillAndStrokePs[3], fillAndStrokePaint);
canvas.drawRect(strokePs[0], strokePs[1], strokePs[2], strokePs[3], strokePaint);
}
}
円/楕円
java
public class CircleView extends View {
private Paint circlePaint;
private Paint ellipsePaint;
private final RectF rectf = new RectF(400, 100, 900, 400);
public CircleView(Context context) {
super(context);
}
public CircleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
circlePaint = new Paint();
circlePaint.setColor(Color.CYAN);
circlePaint.setStrokeWidth(20);
circlePaint.setStyle(Paint.Style.FILL_AND_STROKE);
ellipsePaint = new Paint();
ellipsePaint.setColor(Color.BLUE);
ellipsePaint.setStrokeWidth(20);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
canvas.drawCircle(200, 200, 100, circlePaint);
canvas.drawOval(rectf, ellipsePaint);
}
}
円弧
java
public class ArcView extends View {
private Paint paint;
private Paint centerLinePaint;
private final RectF rectf = new RectF(300, 200, 800, 700);
float[] canterLinePositions = {100.0f, 450.0f, 1000.0f, 450.0f};
public ArcView(Context context) {
super(context);
}
public ArcView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
centerLinePaint = new Paint();
centerLinePaint.setColor(Color.WHITE);
centerLinePaint.setStrokeWidth(2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
float startAngle = 90.0f;
float sweepAngle = 150.0f;
canvas.drawArc(rectf, startAngle, sweepAngle, true, paint);
canvas.drawLine(
canterLinePositions[0], // 開始点X座標
canterLinePositions[1], // 開始点Y座標
canterLinePositions[2], // 終了点X座標
canterLinePositions[3], // 終了点Y座標
centerLinePaint
);
}
}
テキスト
java
public class TextView extends View {
private final String TAG = getClass().getSimpleName();
private Paint paint;
public TextView(Context context) {
super(context);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.WHITE);
paint.setStrokeWidth(5);
// フォントの種類
paint.setTypeface(Typeface.SANS_SERIF);
// テキストサイズ(sp)
paint.setTextSize(40.0f);
// 表示位置
paint.setTextAlign(Paint.Align.CENTER);
// 水平方向への拡大率
paint.setTextScaleX(1.0f);
// 水平方向にずらす度合い
paint.setTextSkewX(0.0f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw: ");
canvas.drawColor(Color.DKGRAY);
canvas.drawText("TextViewテスト", 300, 300, paint);
}
}
画像
java
public class ImageView extends View {
private Paint paint;
private Bitmap baseBmp;
private Rect rect = new Rect(150, 100, 450, 400);
private RectF rectF = new RectF(0, 400, 800, 800);
public ImageView(Context context) {
super(context);
}
public ImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
baseBmp = BitmapFactory.decodeResource(getResources(), R.drawable.luffy);
paint = new Paint();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
canvas.drawBitmap(baseBmp, 0, 10, paint);
canvas.drawBitmap(baseBmp, rect, rectF, paint);
}
}
拡大・縮小・回転
java
public class OperationView extends View {
private Paint originPaint;
private Paint paint;
public OperationView(Context context) {
super(context);
}
public OperationView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.CYAN);
paint.setStrokeWidth(5);
originPaint = new Paint();
originPaint.setColor(Color.LTGRAY);
originPaint.setStrokeWidth(5);
originPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
canvas.translate(200, 200);
canvas.scale(1.5f, 1.5f);
// 回転後
canvas.rotate(60);
canvas.drawRect(0, 0, 200, 200, paint);
canvas.rotate(-60);
canvas.drawRect(0, 0, 200, 200, originPaint);
canvas.skew(-0.1f, 0.5f);
canvas.drawRect(0, 500, 200, 700, paint);
canvas.skew(+0.1f, -0.5f);
canvas.drawRect(0, 500, 200, 700, originPaint);
}
}
パス
java
public class PathView extends View {
private Paint paint;
private Path path;
public PathView(Context context) {
super(context);
}
public PathView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
path = new Path();
paint.setColor(Color.CYAN);
paint.setStrokeWidth(20);
paint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.DKGRAY);
// 平たい角
path.reset();
path.moveTo(210, 400);
path.lineTo(240, 750);
path.lineTo(270, 400);
paint.setStrokeJoin(Paint.Join.BEVEL);
canvas.drawPath(path, paint);
// 鋭角
path.reset();
path.moveTo(400, 400);
path.lineTo(440, 750);
path.lineTo(470, 400);
paint.setStrokeMiter(30);
paint.setStrokeJoin(Paint.Join.MITER);
canvas.drawPath(path, paint);
// 丸角
path.reset();
path.moveTo(600, 400);
path.lineTo(640, 750);
path.lineTo(670, 400);
paint.setStrokeJoin(Paint.Join.ROUND);
canvas.drawPath(path, paint);
}
}
属性の追加
xml
<com.example.canvastestjava.custom_view.CanvasAttrView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:color="@color/purple_200"
app:type="circle"/>
java
public class CanvasAttrView extends View {
// 属性値のフォールド
private int color = Color.BLACK;
private int type = 0;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
// 再描画
invalidate();
requestLayout();
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
// 再描画
invalidate();
requestLayout();
}
private Paint paint = new Paint();
public CanvasAttrView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 属性取得
TypedArray typedArray = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.CanvasAttrView, 0, 0);
try {
color = typedArray.getColor(R.styleable.CanvasAttrView_color, Color.BLACK);
type = typedArray.getInt(R.styleable.CanvasAttrView_type, 0);
} finally {
typedArray.recycle();
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(color);
paint.setStrokeWidth(100);
switch (type) {
case 0:
canvas.drawCircle(200, 200, 100, paint);
break;
case 1:
canvas.drawRect(100, 100, 400, 400, paint);
break;
case 2:
canvas.drawLine(10, 20, 800, 500, paint);
break;
default:
break;
}
}
}
お絵描きアプリ
MotionEvent(主なメソッド)
メソッド | 概要 | |
---|---|---|
getAction | タッチの種類 | |
ACTION_DOWN | ||
ACTION_MOVE | ||
ACTION_UP | ||
ACTION_CANCEL | ||
getDownTime | タッチし続けた時間(ミリ秒) | |
getEdgeFlags | スクリーンの端を判定 | |
EDGE_TOP | 上端に到達 | |
EDGE_BOTTOM | 下端に到達 | |
EDGE_LEFT | 左端に到達 | |
EDGE_RIGHT | 右端に到達 | |
getX | タッチされた位置(X軸) | |
getY | タッチされた位置(Y軸) |
onTaxhEventメソッド
アクション | 処理 |
---|---|
ACTION_DOWN | movetToでパスの起点を定義 |
ACTION_MOVE | lineToで前の座標からの直線を描画 |
ACTION_UP | lineToで前の座標から終点までの直線を描画 |
マルチタッチ関連のメソッド
メンバー | 概要 |
---|---|
getpointerCount | タッチされたポイント数 |
getpointerId(int pointerIndex) | 引数番目のポインターidを取得 |
findPointerIndex(int id) | ポインターidからポインターを検索 |
java
public class DrawingView extends View {
private final String TAG = getClass().getSimpleName();
private Path path;
private Paint paint;
public DrawingView(Context context) {
super(context);
}
public DrawingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStrokeWidth(3);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
path = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawPath(path, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
path.moveTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
path.lineTo(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
performClick();
path.lineTo(event.getX(), event.getY());
break;
default:
break;
}
// onDrawメソッドが呼ばれる(ビューを無効化し再描画をOSに通知)
invalidate();
return true;
}
/**
* performClick
* 呼び出し元からOnClickListenerを利用している場合必要
*
* @return true
*/
@Override
public boolean performClick() {
super.performClick();
return true;
}
public void clearCanvas() {
Log.d(TAG, "clearCanvas: ");
path.reset();
invalidate();
}
}
Discussion