Androidでドラッグできる画像を作りたい

サンプル動画(上のやつ)

http://voidy21.appspot.com/swf/drag.swf

最初に

Androidの開発は初めてなので色々間違っていることもあると思います。
というか久しぶりにJavaを動かした気がします!
あと実機が無いので実機でちゃんと動くかどうかは保証しかねます!

どんなアプリか?

  • 画像を押しながら動かすとその通り動く
  • トラックボール*1を動かすと画像が拡大縮小する
  • 画像の上下関係を把握して反映させている(自然な動き)

クラス構成

DragImage
android.app.Activityクラスを継承したもの。main文のようなもの
DragImageView
android.view.Viewクラスを継承したもの。androidの画面全体を掌握するビュー
DraggableBitmap
android.view.Viewクラスを継承したもの。1つ1つの画像に対応する
ControlPriority
画像の上下関係を調整するクラス。優先順位を付けてクリックされたものを正しく呼び出す管理クラス

コメント

  • Swingのrepaint()と対応するものはinvalidate()かな?
  • 本当は画像自身がクリックされたかどうか判定するような機構を作りたかったけど、画面全体からeventを受け流す形で落ち着いてしまった。

ソース

DragImage.java
package net.voidy21.dragimage;

import android.app.Activity;
import android.os.Bundle;

public class DragImage extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
        setContentView(new DragImageView(this));
    }
}
DragImageView.java
package net.voidy21.dragimage;

import android.content.res.Resources;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.View;

public class DragImageView extends View{
	private ControlPriority cp;
	
	public DragImageView(Context context) {
		super(context);
		setFocusable(true);
		cp = new ControlPriority(context);
		//画像の読み込み
		Resources res = context.getResources();
		Bitmap image1 = BitmapFactory.decodeResource(res,R.drawable.oda);
		Bitmap image2 = BitmapFactory.decodeResource(res,R.drawable.icon);
		Bitmap image3 = BitmapFactory.decodeResource(res,R.drawable.hiyoko);
		cp.add(image1);
		cp.add(image2);
		cp.add(image3);
		cp.initPriority();
	}
	
   	@Override
	public void onDraw(Canvas canvas) {
   		cp.drawBitmaps(canvas);
	}
   	@Override
   	public boolean onTouchEvent(MotionEvent event){
   		switch(event.getAction()){
   		case MotionEvent.ACTION_DOWN:
   			cp.callClickEvent(event);
			break;
   		case MotionEvent.ACTION_UP:
   		case MotionEvent.ACTION_MOVE:
   			cp.callMoveEvent(event);
   			break;
   		}
		invalidate();
		return true;
   	}
	@Override
	public boolean onTrackballEvent(MotionEvent event) {
		cp.callTouchEvent(event);
		invalidate();
		return true;
	}
}
DraggableBitmap.java
package net.voidy21.dragimage;

import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;

public class DraggableBitmap extends View{
	private Bitmap bitmap;
	private int imageWidth = 0;
	private int imageHeight = 0;
	private Rect src;
	private Rect dst;
	private int touchAction;
	private int ballAction;
	private float scaleX = 1.0f;
	private float scaleY = 1.0f;
	private boolean isDragable = false;
	private Point preXY;
	private Point touchXY;
	private int priority = -1;
	private Paint paint;
	
	public DraggableBitmap(Context context) {
		super(context);
		paint = new Paint();
		preXY = new Point(0,0);
		touchXY = new Point(0,0);
		paint.setAntiAlias(true);
		//フォーカス指定
		setFocusable(true);
	}

	public void setBitmap(Bitmap bmp) {
		if( bmp == null ) return;
		bitmap = bmp;
		imageWidth = bitmap.getWidth();
		imageHeight = bitmap.getHeight();
		src = new Rect(0, 0, imageWidth, imageHeight);
		dst = new Rect(src);
	}
		
	public void move(int x, int y) {
		dst.left = x;
		dst.top = y;
		dst.right = x + imageWidth;
		dst.bottom = y + imageHeight;
		invalidate();
	}
	
	public int getImageWidth() {
		return imageWidth;
	}
	
	public int getImageHeight() {
		return imageHeight;
	}
	
	public void scale() {
		dst.right = (int)(src.right * scaleX);
		dst.bottom = (int)(src.bottom * scaleY);
		imageWidth = dst.width();
		imageHeight = dst.height();
		invalidate();
	}

	@Override
	public void onDraw(Canvas canvas) {
		if( bitmap == null ) return;
		canvas.drawBitmap(bitmap, src, dst, paint);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		touchAction = event.getAction();
		int x = (int)event.getX();
		int y = (int)event.getY();
		if( touchAction == MotionEvent.ACTION_MOVE && isDragable ){
			touchXY.set(x, y);
			int newX = dst.left + (touchXY.x - preXY.x);
			int newY = dst.top + (touchXY.y - preXY.y);
			move(newX,newY);
			preXY.set(touchXY.x,touchXY.y);
		}
		if(isTouchImage(x, y)){
			if( touchAction == MotionEvent.ACTION_DOWN ){
				preXY.set(x, y);
				isDragable = true;
			}else if( touchAction == MotionEvent.ACTION_UP ) {
				isDragable = false;
			}
		}else{
			return false;
		}
		return true;
	}
	
	@Override
	public boolean onTrackballEvent(MotionEvent event) {
		ballAction = event.getAction();
		int x = (int)event.getX();
		int y = (int)event.getY();
		if( ballAction == MotionEvent.ACTION_MOVE ){
			float newScaleX = scaleX+(float)(x/20.0f);
			float newScaleY = scaleY+(float)(y/20.0f);
			if(newScaleX > 0.4f && newScaleY > 0.4f)
				setScale(newScaleX,newScaleY);
		}
		return true;
	}
	
	private boolean isTouchImage(int x,int y) {
		int imgX = dst.left;
		int imgY = dst.top;
		return x-imgX >= 0 &&
                       x-imgX < dst.width() &&
		       y-imgY >= 0 &&
                       y-imgY < dst.height();
	}
	
	public void setPriority(int priority) {
		this.priority = priority;
	}

	public int getPriority() {
		return priority;
	}
	
	public void downPriority() {
		--priority;
	}
	
	public void setScale(float scaleX,float scaleY){
		this.scaleX = scaleX;
		this.scaleY = scaleY;
		scale();
	}

}
ControlPriority.java
package net.voidy21.dragimage;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.view.MotionEvent;

public class ControlPriority {
	private ArrayList<DraggableBitmap> dBitmaps;
	private int draggingNum;
	private Context context;
	
	public ControlPriority(Context context) {
		this.context = context;
		dBitmaps = new ArrayList<DraggableBitmap>();
	}
	
	public void initPriority() {
		int i = 0;
		for( DraggableBitmap dBmp : dBitmaps ){
			dBmp.setPriority(i++);
		}
	}
	
	public void add(DraggableBitmap dBitmap){
		dBitmaps.add(dBitmap);
	}
	
	public void add(Bitmap bitmap) {
		DraggableBitmap dBitmap = new DraggableBitmap(context);
		dBitmap.setBitmap(bitmap);
		dBitmaps.add(dBitmap);
	}
	
	public void drawBitmaps(Canvas canvas) {
		for( int i = dBitmaps.size()- 1; i >= 0; --i ) {
   			dBitmaps.get(i).draw(canvas);
   		}
	}
	
	public void callClickEvent(MotionEvent event){
		int size = dBitmaps.size();
		for( int i = 0; i < size; ++i ) {
			if( dBitmaps.get(i).onTouchEvent(event) ){
				draggingNum = i;
				break;
			}
		}
		int dPriority = dBitmaps.get(draggingNum).getPriority();
		for( DraggableBitmap dBmp : dBitmaps )
			if(dBmp.getPriority() > dPriority)
				dBmp.downPriority();				

		dBitmaps.get(draggingNum).setPriority(size);
                //ソート
		Collections.sort(dBitmaps, new Comparator<DraggableBitmap>(){
			public int compare(DraggableBitmap db1, DraggableBitmap db2) {
				return db2.getPriority() - db1.getPriority();
 			}
 		});
	}
	
	public void callMoveEvent(MotionEvent event) {
		dBitmaps.get(0).onTouchEvent(event);
	}

	public void callTouchEvent(MotionEvent event) {	
		dBitmaps.get(0).onTrackballEvent(event);
	}
}

最後に

次はViewじゃなくてSurfaceViewってやつを使ってみたいです!
あと、実機がほしいです!

*1:エミュレータだとdelete押しながらマウスを動かすと動きます。動画では左上にたまに写る丸い斑点がそうです。