AndroidのViewに絵を描く

# 概要
Viewの中に、何か自分で簡単なアニメーションを作りたいとか、何か表示したいとかそういう時に。
SurfaceViewを使用するのがいっぱんてきなのかもしれませんが、[SurfaceViewは遅い](https://blog.keiji.io/2015/12/mincomi-adventcalendar-20.html)なんて記事を見たので、Viewで実装してみました。

# Viewのサブクラスを作る
```java
public class CustomView extends View {
}
```

# 3つのコンストラクタを作る
newされる場合、layout.xmlに記述される場合などで呼ばれるコンストラクタが変わるので3つ実装しておくのが無難。

```java
public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}
```

# onDrawを実装する
Viewに描きたい内容はonDrawで記述します。
onDrawはViewが作られたとき、invalidateが呼ばれたときに呼ばれます。
実際にはPaintクラスなどを使ってonDrawメソッドに渡されてくるcanvasに描くことになります。
[Paintクラス](https://developer.android.com/reference/android/graphics/Paint.html)と[Canvasクラス](https://developer.android.com/reference/android/graphics/Canvas.html)の詳しい説明はそれぞれのJavadocを見てください。どちらもAPI Level 1からある老舗のクラスなので、安心して使えます。
以下の例では簡単な赤い点を打ちます。

```java
public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = this.createRedPaint();
        canvas.drawPoint(20, 20, paint);
    }
    
    private Paint createRedPaint() {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        // 以下の値が小さいと点が見えない可能性があります
        paint.setStrokeWidth(10);
    }
}
```
これで、CustomViewが表示されたときに赤い点が描画されます。

# 描画内容を動的に更新する
動的に描画内容を更新したい場合は、外から更新内容を与えてinvalidateを呼ぶ。
以下ではdrawPointメソッドを作って、外から呼び出されることを想定しています。

```java
public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = this.createRedPaint();
        canvas.drawPoint(20, 20, paint);
    }
    
    private Paint createRedPaint() {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.RED);
        // 以下の値が小さいと点が見えない可能性があります
        paint.setStrokeWidth(10);
    }

    public void drawPoint(Point point) {
        Paint paint = this.createRedPaint();
        canvas.drawPoint(point.x, point.y, paint);
        invalidate();                 // これを呼ばないと描画内容が更新されない
    }
}
```
私が試した感じだと上記の内容で描画が更新されますが、[ここ](http://tomatojuice.sblo.jp/article/143343987.html)にあるように、より上位のViewを更新しないと下位のViewが更新されない可能性もありますので、その場合はinvalidateの呼び出しを変えないといけないかもしれません。

# 結論
これと同じ要領で、秒間50回ほど、ランダムに1000点を描画するプログラムを書いてみましたが十分高速に表示されてました。
そんなに難しくないので、SurfaceView使わなくても簡単なものならViewを継承したサブクラスで実装してみるものいいかもしれません。