前回作成したアンドゥ・リドゥ機能をAndroidのアプリに組み込んでみよう。
簡単なドローアプリを作ってみることにする。
画面は以下のような構成とする。
|
|
UndoとRedoの二つのボタンがある。背景全体はカスタムビューとなっていて、タッチ&ムーブで
絵を書くことが出来る。
まずはレイアウトのxmlを示す。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.lesson.DrawingView
android:id="@+id/drawingView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
<Button
android:id="@+id/btnUndo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="undo"
/>
<Button
android:id="@+id/btnRedo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/btnUndo"
android:text="redo"
/>
</RelativeLayout>
そして、メインのActivityは次のようなコードとなる。
MainActivity.java
package com.lesson;
import com.lesson.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final DrawingView view = (DrawingView)findViewById(R.id.drawingView);
findViewById(R.id.btnUndo).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
view.undo();
}
});
findViewById(R.id.btnRedo).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
view.redo();
}
});
}
}
最後に、このアプリの要となるカスタムビューのソースを示す。
DrawingView.java
package com.lesson;
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class DrawingView extends View {
private final HistoryStack<ArrayList<PointF>> history = new HistoryStack<ArrayList<PointF>>();
private ArrayList<PointF> currentStroke;
public DrawingView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
/**
* アンドゥ
*/
public void undo(){
history.undo();
invalidate();
}
/**
* リドゥ
*/
public void redo(){
history.redo();
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if( event.getAction() == MotionEvent.ACTION_DOWN){
// 新しい描画
currentStroke = new ArrayList<PointF>();
return true;
}
else if(event.getAction() == MotionEvent.ACTION_MOVE){
currentStroke.add(new PointF(event.getX(),event.getY()));
invalidate();
return true;
}
else if(event.getAction()==MotionEvent.ACTION_UP){
history.add(currentStroke);
currentStroke = null;
invalidate();
return true;
}
return super.onTouchEvent(event);
}
/**
* PointFの配列を元に一連の線を描画する
* @param canvas
* @param paint
* @param stroke
*/
private void drawStroke(Canvas canvas,Paint paint,ArrayList<PointF> stroke){
PointF startPoint = null;
for(PointF pf:stroke){
if( startPoint != null){
canvas.drawLine(startPoint.x, startPoint.y, pf.x, pf.y, paint);
}
startPoint = pf;
}
}
private final Paint paint = new Paint();
{
paint.setColor(Color.CYAN);
paint.setStrokeWidth(1.f);
}
@Override
protected void onDraw(Canvas canvas) {
// 履歴に入っている線を描画する
for(final ArrayList<PointF> stroke:history.iterateUndo()){
drawStroke(canvas,paint,stroke);
}
// 現在描画中の線を描画する
if( currentStroke != null){
drawStroke(canvas,paint,currentStroke );
}
}
}
17行目で宣言している履歴を格納するためのHistoryStackというクラスのソースは前回の記事に示した通りである。
onDrawのタイミングで、履歴にある全てのストロークと描画中のストロークを表示している。
画面のUndoボタンが押されると履歴を一つ戻し、Redoボタンが押されると履歴を一つ進めるようにしている。
1.初期状態
|
2.アンドゥ
|
3.アンドゥ
|
4.アンドゥ
|
5.書き込み
|
このようにして、アンドゥ・リドゥ機能を実現することができた。
ただ、このアプリのアンドゥ・リドゥ機能の回数は無制限である。
廉価版アプリなどで回数制限をかけたいときは、
もう少しコーディングを工夫する必要がありそうだ。