【Android】アンドゥ・リドゥ機能の組み込み

By | 2011年10月15日

前回作成したアンドゥ・リドゥ機能を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.書き込み

このようにして、アンドゥ・リドゥ機能を実現することができた。
ただ、このアプリのアンドゥ・リドゥ機能の回数は無制限である。
廉価版アプリなどで回数制限をかけたいときは、
もう少しコーディングを工夫する必要がありそうだ。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です