Challenge 1 & 2 solution


#1

Here is the solution to challenges 1 and 2. Note that for the second challenge, it crashes if I have more than 2 touches simultaneously.

Also, I added a get / set method for a new int in Box.java called pId(for pointer ID)

Box.java

[code] private int pId;

public Box(PointF origin){
	mOrigin = mCurrent = origin;
}

public int getpId() {
	return pId;
}

public void setpId(int pId) {
	this.pId = pId;
}

[/code]

Note for the first challenge it is important to give your view an ID or onRestoreInstance won’t be able to identify your view.
I did this.setId(VIEW_ID) in the constructor of BoxDrawingView, where VIEW_ID is an arbitrary integer.

BoxDrawingView.java

[code]import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

public class BoxDrawingView extends View {
public static final String TAG = “BoxDrawingView”;
private static final String KEY_INDEX = “Key”;

private static final int VIEW_ID =  699;

private Box mCurrentBox;
private Box[] mCurrentBoxes = new Box[VIEW_ID];
private Paint mBoxPaint;
private Paint mBackgroundPaint;
private ArrayList<Box> mBoxes = new ArrayList<Box>();
private PointF [] mCurr = new PointF[VIEW_ID];


public boolean onTouchEvent(MotionEvent event){
	int pIndex;
	int pId;
	try{
		pIndex = event.getActionIndex();
		pId = event.getPointerId(pIndex);
	}catch(Exception e){
		System.out.println("Caught the exception");
		pIndex = 0;
		pId = 0;
	}
	//PointF curr;
	boolean empty = true;
	
	for(int i = 0; i < mCurrentBoxes.length; i++){
		if(mCurrentBoxes[i] != null){
			empty = false;
			break;
		}
	}
	if(!empty){
		if(event.getPointerCount() >= 2){
			for(Box boxes : mCurrentBoxes){
				if(boxes != null){
					int tempId = boxes.getpId();
					mCurr[tempId] = new PointF(event.getX(tempId),event.getY(tempId));
				}
			}
		}
	}
	
	if(pId == 0)
		mCurr[0] = new PointF(event.getX(),event.getY());
	//Log.i(TAG, "Received event at x=" + curr.x + ", y=" + curr.y +":");

	
	switch(event.getActionMasked()){
		case MotionEvent.ACTION_POINTER_DOWN:
			mCurr[pId] = new PointF(event.getX(pId), event.getY(pId));
			mCurrentBoxes[pId] = new Box(mCurr[pId]);
			mCurrentBoxes[pId].setpId(pId);				
			break;
		case MotionEvent.ACTION_POINTER_UP:
			Log.i(TAG, "Action Pointer UP  Found "
					+"\n Pointer Id is " + pId);
			if(pId == 0){
				mCurrentBox = null;
			}else{
				mBoxes.add(mCurrentBoxes[pId]);
				mCurrentBoxes[pId] = null;
				mCurr[pId] = null;
			}
			Log.i(TAG, "ACTION POINTER UP DONE");
			break;
		case MotionEvent.ACTION_DOWN:
			Log.i(TAG," ACTION_DOWN");
			//Reset drawing state
			mCurrentBox = new Box(mCurr[0]);
			mBoxes.add(mCurrentBox);
			break;
		case MotionEvent.ACTION_MOVE:
		//	Log.i(TAG, "pIndex is " + pIndex + " andd pId is " + pId);
			for(Box boxes : mCurrentBoxes){
				if(boxes != null){
				if(boxes.getpId()!= 0){
					int tempId = boxes.getpId();
					Log.i(TAG, "Temp ID is " + tempId);
					if(mCurrentBoxes[tempId] != null){
						mCurrentBoxes[tempId].setCurrent(mCurr[tempId]);
						Log.i(TAG,"mCurrent[pId] is X " + mCurr[tempId].x + " Y is " +mCurr[tempId].y);
					}
				}
				}else{
					if(mCurrentBox != null && pId == 0){
						mCurrentBox.setCurrent(mCurr[0]);
					}
				}
				
			}
			invalidate();
			break;
		case MotionEvent.ACTION_UP:
			Log.i(TAG,"ACTION_UP");
			if(pId != 0){
				mBoxes.add(mCurrentBoxes[pId]);
				mCurrentBoxes[pId] = null;
				mCurr[pId] = null;
				break;
			}else
			mCurrentBox = null;
			break;
		case MotionEvent.ACTION_CANCEL:
			Log.i(TAG,"ACTION_CANCEL");
			mCurrentBox = null;
			mCurrentBoxes[event.getPointerId(event.getActionIndex())] = null;
			break;
	}
	return true;
	
	
}

@Override
protected Parcelable onSaveInstanceState(){
	Bundle onSaveInstanceState = new Bundle();
	onSaveInstanceState.putParcelable(KEY_INDEX, super.onSaveInstanceState());
	onSaveInstanceState.putSerializable(KEY_INDEX, mBoxes);
	
	return onSaveInstanceState;
}

@Override
protected void onRestoreInstanceState(Parcelable state){
	
	Bundle onSaveInstanceState = (Bundle)state;
	mBoxes = (ArrayList<Box>)onSaveInstanceState.getSerializable(KEY_INDEX);
	super.onRestoreInstanceState(onSaveInstanceState.getParcelable(KEY_INDEX));
	invalidate();
}



//Used when creating the view in code
public BoxDrawingView(Context context){
	this(context, null);
}

//Used when inflating the view from XML
public BoxDrawingView(Context context, AttributeSet attrs){
	super(context,attrs);
	
	this.setId(VIEW_ID);
	
	//Paint the boxes a nice semitransparent red(ARGB)
	mBoxPaint = new Paint();
	mBoxPaint.setColor(0x22ff0000);
	
	//Paint the background off-white
	mBackgroundPaint = new Paint();
	mBackgroundPaint.setColor(0xfff8efe0);
}

@Override
protected void onDraw(Canvas canvas){
	//Fill the background
	canvas.drawPaint(mBackgroundPaint);
	
	for (Box box : mBoxes){
		float left = Math.min(box.getOrigin().x, box.getCurrent().x);
		float right = Math.max(box.getOrigin().x, box.getCurrent().x);
		float top = Math.min(box.getOrigin().y, box.getCurrent().y);
		float bottom = Math.max(box.getOrigin().y,box.getCurrent().y);
		
		canvas.drawRect(left, top,right,bottom, mBoxPaint);
	}
	for (Box boxes : mCurrentBoxes){
		if(boxes != null){
			System.out.println("Drawing mCurrentBoxes");
			System.out.println("Drawing Coor X ori " +  boxes.getOrigin().x
					+ " Y ori " + boxes.getOrigin().y + " Y Curr " + boxes.getCurrent().y
					+ " X Curr " + boxes.getCurrent().x);
			float left = Math.min(boxes.getOrigin().x, boxes.getCurrent().x);
			float right = Math.max(boxes.getOrigin().x, boxes.getCurrent().x);
			float top = Math.min(boxes.getOrigin().y, boxes.getCurrent().y);
			float bottom = Math.max(boxes.getOrigin().y,boxes.getCurrent().y);
			
			canvas.drawRect(left, top,right,bottom, mBoxPaint);
			System.out.println("Done drawing");
		}
		
	}
}

}[/code]

Obviously not the cleanest code, but hopefully it will inspire some ideas in your own solutions.


#2

it is easier that you can have your boxDrawingView instance get an id instead of having all of the boxes have an id respectively.
it is a good hobby to make every view in the xml layout have an id.

<com.star.draganddraw.BoxDrawingView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/boxDrawingView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

and there is no need to use invalidate() in the onRestoreInstanceState() method. Because every time you rotate your screen, the activity will destroy and recreate itself which will call the View’s draw() method automatically.