お絵かきアプリを作成してみたいと思います。
参考:https://gist.github.com/547660
前回作成したViewをイベントによって、再描画をする。を元に作成をしてみたいと思います。
まず、前回の丸を描く際に利用した方法を用います。
今回は線を描く方法を考えたいと思います。
線を描く時にドロイド君を書いた時のdrawLineを用いましたが、連続した線を描く為には、drawPathを用います。
drawPathはpathで作成した点をなぞる形で、線を描いてくれます。
またペイントの設定は下記にしました。
paint.setAntiAlias(true); paint.setColor(Color.LTGRAY); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(6); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeJoin(Paint.Join.ROUND);
アンチエイリアス、色をライトグレー、図形を線のみ、線の太さを6、端点を丸く、つなぎ目を丸く
今回利用はしてないのですが、下記のような要素も指定できるようです。
破線(参照:)
タッチのイベントでは、ここで作成をする線のPathを作成します。
public boolean onTouchEvent(MotionEvent e){ posx = e.getX(); posy = e.getY(); switch(e.getAction()){ case MotionEvent.ACTION_DOWN: //最初のポイント path = new Path(); path.moveTo(posx , posy ); break; case MotionEvent.ACTION_MOVE: //途中のポイント path.lineTo(posx, posy); invalidate(); break; case MotionEvent.ACTION_UP: //最後のポイント path.lineTo(posx, posy); invalidate(); break; default: break; } return true; }
以上で、タッチをしたところから、なぞる線を描くことができました。動かす度に更新で、再描画されます。
再描画は以下のファンクションで実行します。
//public @Override protected void onDraw(Canvas canvas){ Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.LTGRAY); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(6); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeJoin(Paint.Join.ROUND); if(path != null){ canvas.drawPath(path, paint); } }
線を描けたのですが、この線に関して次の線を描いた時に消えてしまいます。
次線が記載された時に前の線を残す方法があります。
1、pathを配列で保持しておく。
2、canvasのキャプチャをとって、次の線が描かれたらキャプチャと線を重ねる。
です。今回は2の方法で作成をします。
キャプチャを取る方法ですが、通常viewはキャッシュが効かないようになっていますが、キャッシュをONにします。
setDrawingCacheEnabled(true);
ここで作成したキャッシュをビットマップに変換をします。
bitmap = Bitmap.createBitmap(getDrawingCache());
キャッシュを利用していると負荷がかかるので、キャッシュを切ります。
setDrawingCacheEnabled(false);
以上で現状のキャプチャが取れます。これを指を離した瞬間に取得します。
public boolean onTouchEvent(MotionEvent e){ posx = e.getX(); posy = e.getY(); switch(e.getAction()){ case MotionEvent.ACTION_DOWN: //最初のポイント path = new Path(); path.moveTo(posx , posy ); break; case MotionEvent.ACTION_MOVE: //途中のポイント path.lineTo(posx, posy); invalidate(); break; case MotionEvent.ACTION_UP: //最後のポイント path.lineTo(posx, posy); setDrawingCacheEnabled(true); bitmap = Bitmap.createBitmap(getDrawingCache()); setDrawingCacheEnabled(false); invalidate(); break; default: break; } return true; }
これで描かれた線、及びその背景をbitmapの中にいれる事ができました。
onDrawでこのキャッシュを描画するファンクションを足してみます。
//public protected void onDraw(Canvas canvas){ super.onDraw(canvas); if(bitmap != null){ canvas.drawBitmap(bitmap, 0, 0, null); } Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.LTGRAY); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(6); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeJoin(Paint.Join.ROUND); if(path != null){ canvas.drawPath(path, paint); } }
足しました。これで完成です。
しかし、これだと、少し、動きが硬い部分が出てしまいます。
フレームレートの問題もあるかと思いますが、ペンの動きを指の動きに追う動きにしたいと思います。
仕組みとしては、前回線の位置と指の位置の間に線を移動させます。そうする事で、ガタガタしてしまう点も滑らかな曲線になってくれます。
case MotionEvent.ACTION_MOVE: //途中のポイント posx += (e.getX()-posx)/1.4; posy += (e.getY()-posy)/1.4; path.lineTo(posx, posy); invalidate(); break;
下記にソースを置いておきます。
package in.andante.touchtest; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.view.MotionEvent; import android.view.View; //描画を行うView public class TouchCircle extends View{ private float posx = 0f; //イベントが起きたX座標 private float posy = 0f; //イベントが起きたY座標 private Path path = null; //パス private Bitmap bitmap = null; //Viewの状態を保存するためのBitmap public TouchCircle(Context context){ super(context); } public boolean onTouchEvent(MotionEvent e){ //イベントのタイプごとに処理を設定 switch(e.getAction()){ case MotionEvent.ACTION_DOWN: //最初のポイント path = new Path(); posx = e.getX(); posy = e.getY(); path.moveTo(e.getX(), e.getY()); break; case MotionEvent.ACTION_MOVE: //途中のポイント posx += (e.getX()-posx)/1.4; posy += (e.getY()-posy)/1.4; path.lineTo(posx, posy); invalidate(); break; case MotionEvent.ACTION_UP: //最後のポイント path.lineTo(e.getX(), e.getY()); //キャッシュの中からキャプチャを作成するので、その一瞬の為にキャッシュをON setDrawingCacheEnabled(true); bitmap = Bitmap.createBitmap(getDrawingCache()); setDrawingCacheEnabled(false); invalidate(); break; default: break; } return true; } @Override protected void onDraw(Canvas canvas){ super.onDraw(canvas); if(bitmap != null){ canvas.drawBitmap(bitmap, 0, 0, null); } Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.LTGRAY); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(6); paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeJoin(Paint.Join.ROUND); if(path != null){ canvas.drawPath(path, paint); } } }
です。
また、背景を白くメニューバーなどを隠すテーマを適用します。
manifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="in.andante.touchtest" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="7" /> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".TouchTest" android:label="@string/app_name" android:theme="@android:style/Theme.Light.NoTitleBar.Fullscreen"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
以上で簡単なお絵かきアプリを制作できました。