AndroidStudioで簡単なお絵かきアプリを作ってみよう

この記事は[SLP_KBIT Advent Calendar 2016](http://qiita.com/advent-calendar/2016/slp_kbit)の9日目の記事です。

## はじめに
Googleが提供しているAndroidアプリ開発環境 AndroidStudioで「簡単なお絵かきアプリ」を作ります。
AndroidStudioをさわり始めた方でも簡単に作ることができると思います。

## アプリの仕様
今回はお絵かきアプリとしての最低限の機能を持ったものを作ります。
機能は以下のようになります。

1. 画面上をタッチ&スライドすることで単色の線を描画できる
2. キャンバス上の全ての描画を消す

お絵かきアプリなので1つ目の機能は必須ですよね。また、2つ目の機能は新しい絵を描きたいとき、
毎回アプリを再起動しなおすのは面倒なので実装することにしました。

## 開発環境

開発にはAndroidStudio 2.2.3 を用いました。
また、実機テストはLollipop 5.0.2のXperiaZ3で行いました。

## 描画用View

まず、絵を描く際のキャンバスに相当するCanvasViewを実装します。
CanvasViewはViewを継承したクラスです。
CanvasViewのための新しいJavaファイルを作成してください。
作成するにはProjectビューで追加したいフォルダを右クリックしてNew>Java Classを指定してください。
<img src="https://qiita-image-store.s3.amazonaws.com/0/151965/eb1035ef-3102-6392-df90-90bf65ff2fef.png" alt="javaFile" width="45%">

今回のアプリでは曲線を直線の集合で表現することにします。
なので、フィールドとしてPathクラスのインスタンスを格納するためのリスト pathListを用意します。
Viewのコンストラクタは引数が1つのものと2つのものがありますが、今回は引数が2つのコンストラクタが使われます。
そのあたりを説明すると、記事がもう一つかける量になるので省略します。
コンストラクタでは、各フィールドの初期化とペンの設定を行っています。
各処理はコード中のコメントを参考にしてください。

```java:CanvasView.java
public class CanvasView extends View {

    private final ArrayList<Path> pathList;  // 直線リスト
    private final Paint paint;

    //======================================================================================
    //--  コンストラクタ
    //======================================================================================
    public CanvasView(Context context) {
        super(context);

        //-- 初期化
        //- Path関連
        pathList = new ArrayList<Path>();   // リストの作成

        //- Paint関連
        paint = new Paint();
        paint.setColor(Color.RED);          // 色の指定
        paint.setStyle(Paint.Style.STROKE); // 描画設定を'線'に設定
        paint.setAntiAlias(true);           // アンチエイリアスの適応
        paint.setStrokeWidth(10);           // 線の太さ
    }

    public CanvasView(Context context, AttributeSet attrs) {
        super(context, attrs);

        //-- 初期化
        //- Path関連
        pathList = new ArrayList<Path>();   // リストの作成

        //- Paint関連
        paint = new Paint();
        paint.setColor(Color.RED);          // 色の指定
        paint.setStyle(Paint.Style.STROKE); // 描画設定を'線'に設定
        paint.setAntiAlias(true);           // アンチエイリアスの適応
        paint.setStrokeWidth(10);           // 線の太さ
    }
}
```
次に、各種メソッドを実装していきます。
メソッドは描画、タッチイベント、削除の3種類です。
以下の3つメソッドのコードはCanvasView.javaに追加してください。

まずは、描画メソッドから。

```java:CanvasView.java               
  //======================================================================================
  //--  描画メソッド       
  //======================================================================================
  @Override
  protected void onDraw(Canvas canvas) {
      for ( Path path : pathList ) {
          canvas.drawPath(path, paint);   // Pathの描画
      }

      invalidate();   // 再描画
  }
```
ここではリスト pathListに登録されているPathを順番に描画しています。
Viewでは描画に変更があった場合、invalidate()メソッドを呼び出してやり、強制的に再描画してやる必要があるので、忘れないようにしましょう。

次はタッチイベントメソッドです。

```java:CanvasView.java
  //======================================================================================
  //--  タッチイベント  
  //=====================================================================================
  private Path drawingPath;

  @Override
  public boolean onTouchEvent(MotionEvent event) {

      switch ( event.getAction() ) {
          case MotionEvent.ACTION_DOWN:                             //- 画面をタッチしたとき
                  drawingPath = new Path();                         // 新たなPathのインスタンスの作成
                  drawingPath.moveTo(event.getX(), event.getY());   // 始点を設定
                  pathList.add(drawingPath);                        // リストにPathを追加
                  break;
          case MotionEvent.ACTION_UP:                               //- 画面から指を離したとき
                  drawingPath.moveTo(event.getX(), event.getY());   // 移動先の追加
                  break;
          case MotionEvent.ACTION_MOVE:                             //- タッチしながら指をスライドさせたとき
                  drawingPath.lineTo(event.getX(), event.getY());   // 移動先の追加
                  break;
      }

      return true;   /* 返却値は必ず "true" にすること!! */
  }
```
このメソッドでは画面をタッチなどしたときの処理を定義しています。
メソッドの引数であるeventのgetAction()メソッドを呼び出すことで、画面に対して、どのような動作が行われたか知ることが出来ます。
「タッチしたとき」、「離したとき」、「スライドしたとき」の3つの動作に対して、それぞれPathに関する処理を行っています。
また、返却値は必ず"ture"にするようにしましょう。返却値をfalseにした場合、タッチした時の処理しか行われないようになります。
それぞれの処理は上記コードのコメントを参照してください。

最後に削除メソッドです。

```java:CanvasView.java  
  //======================================================================================
  //--  削除メソッド
  //======================================================================================
  public void allDelete() {
      pathList.clear();   // リストが保持しているPathのインスタンスを全て削除
  }
```
このメソッドでは、リスト pathListに登録されている全てのPathを削除します。

## レイアウト
絵を書くためのキャンバスができたので、次は、xmlでそれをどこに配置するかを定義します。
今回はメインのレイアウトファイルactivity_main.xmlに描画用ViewのCanvasViewと削除用ボタンを配置します。
レイアウトファイルactivity_main.xmlは以下の画像の位置にあります。
<img src="https://qiita-image-store.s3.amazonaws.com/0/151965/d26b3acf-d054-e653-f553-20846aee6158.png" alt="layout" width="45%">

以下のコードのようにactivity_main.xmlを変更・追加します。

```xml:activity_main.xml
<!-- LinearLayoutをRelativeLayoutに変更 -->
<!-- android:orientation="vertical"を追加 -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"

    android:orientation="vertical"

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.drawapp.MainActivity">


    <!-- 追加：ここから  -->
    <!-- キャンバス -->
    <!-- パッケージ名を含むクラス名で指定 今回の例だと"com.example.drawapp.CanvasView" -->
    <com.example.drawapp.CanvasView   
        android:id="@+id/canvas_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <!-- キャンバスクリア用ボタン -->
    <Button
        android:id="@+id/clear_button"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_alignBottom="@id/canvas_view"
        android:text="CLEAR"
        />
    <!-- 追加：ここまで  -->

</RelativeLayout>      <!-- LinearLayoutをRelativeLayoutに変更 -->
```

デザインを確認すると以下の画像のような構成になっているはずです。
次はいよいよ作ったViewを画面に表示できるようにします。

<img src="https://qiita-image-store.s3.amazonaws.com/0/151965/fb4623f5-ba90-b056-72b6-f05d062ef7e9.png" alt="Design" width="50%">

## 仕上げ
作ったCanvasViewとボタンをレイアウト通りに表示させるには、メインのActivity(デフォルトのクラス名はMainActivity)を以下のように変更します。

```Java:MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      final CanvasView cv = (CanvasView) findViewById(R.id.canvas_view);

      //-- Button関連
      Button bt = (Button) findViewById(R.id.clear_button); 
      //- 動作設定
      bt.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              cv.allDelete();
          }
      });
    }
}
```

btのsetOnClickListener()でボタンを押したときの処理を定義しています。
ボタンを押したとき、CanvasViewの描画をすべて消すメソッド allDelete()メソッドを呼び出しています。

これでAndroidStudioのエミュレータもしくはAndroidの実機でアプリを起動できれば、
**簡単なお絵かきアプリ完成**です。

<img src="https://qiita-image-store.s3.amazonaws.com/0/151965/0379f1a8-b28c-50a1-cc16-68133f237f48.png" alt="drawApp" width="25%">


## おわりに

無事、アプリは起動しましたか？
今回は2つの機能を持ったお絵かきアプリを作りましたが、
ペンの色の変更や図形の描画など、工夫次第で面白いアプリになると思うのでぜひ挑戦してみてください :smiley: