My Challenge 2 Solution


#1

This challenge was probably the hardest in the book thus far. Now that I look at my code it doesn’t seem too difficult, but having no prior experience in multitouch handling there was a huge learning curve there for me. I’m not sure if I interpreted the instructions of the challenge correctly (they seemed a little vague), but the following is my solution. It works by allowing the user to rotate a rectangle while they are drawing it by repeatedly tapping their second finger.

For Box.java I added getCenter(), setAngle(int), and getAngle():

[code]public class Box {
private PointF mOrigin;
private PointF mCurrent;
private int mAngle;

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

public void setCurrent(PointF current) {
    mCurrent = current;
}

public PointF getOrigin() {
    return mOrigin;
}

public PointF getCurrent() {
    return mCurrent;
}

public PointF getCenter() {
    float middleX = (mCurrent.x + mOrigin.x) / 2.0f;
    float middleY = (mCurrent.y + mOrigin.y) / 2.0f;

    return new PointF(middleX, middleY);
}

public void setAngle(int angle) {
    mAngle = angle;
}

public int getAngle() {
    return mAngle;
}

}[/code]

For BoxDrawingView.java I increase the angle every time a second finger tap is received with ACTION_POINTER_DOWN, then in onDraw I draw every box at the angle saved for that box:

[code]public boolean onTouchEvent(MotionEvent event) {
PointF curr = new PointF(event.getX(), event.getY());

    switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN:
            angle = 0;
            mCurrentBox = new Box(curr);
            mBoxes.add(mCurrentBox);
            break;
        case MotionEvent.ACTION_MOVE:
            if (mCurrentBox != null) {
                mCurrentBox.setCurrent(curr);
                invalidate();
            }
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            angle += 5;
            if (mCurrentBox != null) {
                mCurrentBox.setAngle(angle);
                invalidate();
            }
            break;
        case MotionEvent.ACTION_UP:
            mCurrentBox = null;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            break;
        case MotionEvent.ACTION_CANCEL:
            mCurrentBox = null;
            break;
    }

    return true;
}

@Override
protected void onDraw(Canvas canvas) {
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.save();
        canvas.rotate(box.getAngle(), box.getCenter().x, box.getCenter().y);
        canvas.drawRect(left, top, right, bottom, mBoxPaint);
        canvas.restore();
    }
}[/code]

#2

I use ACTION_MOVE to decide the angle instead the ACTION_POINTER_DOWN

switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                Log.i(TAG, " ACTION_DOWN");
                mCurrentBox = new Box(curr);
                mBoxes.add(mCurrentBox);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i(TAG, " ACTION_MOVE");
                if (mCurrentBox != null) {
                    mCurrentBox.setCurrent(curr);
                    if (event.getPointerCount() == 2) {
                        int angle = (int) (Math.atan((event.getY(1) - event.getY(0)) /
                                        (event.getX(1) - event.getX(0))) * 360 / (2 * Math.PI));
                        mCurrentBox.setAngle(angle);
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                Log.i(TAG, " ACTION_UP");
                mCurrentBox = null;
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.i(TAG, " ACTION_CANCEL");
                mCurrentBox = null;
                break;
        }

The first finger will decide the rec’s location and size. The line of the two fingers will decide the rotate angle. It’s more dynamic.