Drag a rectangle on Canvas Android

1.6k views Asked by At

I'm drawing a rectangle on a Canvas in Android. How do I drag it around on the screen? I have an onTouchEvent() method in my class that listens to MotionEvent(s). How do I use this to drag my rectangle around while preserving it's size. My code is as follows:

public class CustomSquareView3 extends View {

private Rect rect;
private Paint paint;
public static int rotation = 45;

Canvas canvas;

int x1 = 200;
int x2 = 400;
int y1 = 200;
int y2 = 400;

public CustomSquareView3(Context context){
    super(context);

    int x = 50;
    int y = 50;
    int sideLength = 200;
    rect = new Rect(x1,y1, x2, y2);

    paint = new Paint();
    paint.setColor(Color.GRAY);

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.drawColor(Color.WHITE);

    canvas.save();

    canvas.rotate(rotation, (x1+x2)/2, (y1+y2)/2);

    canvas.drawRect(rect, paint);

    Paint paint2 = new Paint();
    paint2.setColor(Color.WHITE);
    paint2.setTextSize(50);

    canvas.drawText("Hi", (x1+x2)/2, (y1+y2)/2, paint2);

    canvas.restore();

}


@Override
public boolean onTouchEvent(MotionEvent event) {

   int motionEvent = event.getAction();
   float xMove = event.getX();
   float yMove = event.getY();

   switch (motionEvent){

       case MotionEvent.ACTION_DOWN : {

           x1 = (int) event.getX();
           y1 = (int) event.getY();

           rect = new Rect(x1, y1, x2, y2);

           break;
       }

       case MotionEvent.ACTION_MOVE : {

           x1 = (int) event.getX();
           y1 = (int) event.getY();

           rect = new Rect(x1, y1, x2, y2);

           break;
       }

       case MotionEvent.ACTION_UP : {

           x1 = (int) event.getX();
           y1 = (int) event.getY();

           rect = new Rect(x1, y1, x2, y2);

           break;

       }

       default:
           return false;
   }

   invalidate();
   return true;
}
}

The above code resizes the rectangle every time I touch the screen. How do I get a smooth drag gesture in the code such that the size of the rectangle is maintained when it is dragged around the screen?

1

There are 1 answers

0
badadin On

See this example:

class ViewPort extends View {
    List<Layer> layers = new LinkedList<Layer>();
    int[] ids = {R.drawable.layer0, R.drawable.layer1, R.drawable.layer2};

    public ViewPort(Context context) {
        super(context);
        Resources res = getResources();
        for (int i = 0; i < ids.length; i++) {
            Layer l = new Layer(context, this, BitmapFactory.decodeResource(res, ids[i]));
            layers.add(l);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for (Layer l : layers) {
            l.draw(canvas);
        }
    }

    private Layer target;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            target = null;
            for (int i = layers.size() - 1; i >= 0; i--) {
                Layer l = layers.get(i);
                if (l.contains(event)) {
                    target = l;
                    layers.remove(l);
                    layers.add(l);
                    invalidate();
                    break;
                }
            }
        }
        if (target == null) {
            return false;
        }
        return target.onTouchEvent(event);
    }
}

class Layer implements MatrixGestureDetector.OnMatrixChangeListener {
    Matrix matrix = new Matrix();
    Matrix inverse = new Matrix();
    RectF bounds;
    View parent;
    Bitmap bitmap;
    MatrixGestureDetector mgd = new MatrixGestureDetector(matrix, this);

    public Layer(Context ctx, View p, Bitmap b) {
        parent = p;
        bitmap = b;
        bounds = new RectF(0, 0, b.getWidth(), b.getHeight());
        matrix.postTranslate(50 + (float) Math.random() * 50, 50 + (float) Math.random() * 50);
    }

    public boolean contains(MotionEvent event) {
        matrix.invert(inverse);
        float[] pts = {event.getX(), event.getY()};
        inverse.mapPoints(pts);
        if (!bounds.contains(pts[0], pts[1])) {
            return false;
        }
        return Color.alpha(bitmap.getPixel((int) pts[0], (int) pts[1])) != 0;
    }

    public boolean onTouchEvent(MotionEvent event) {
        mgd.onTouchEvent(event);
        return true;
    }

    @Override
    public void onChange(Matrix matrix) {
        parent.invalidate();
    }

    public void draw(Canvas canvas) {
        canvas.drawBitmap(bitmap, matrix, null);
    }
}

class MatrixGestureDetector {
    private static final String TAG = "MatrixGestureDetector";

    private int ptpIdx = 0;
    private Matrix mTempMatrix = new Matrix();
    private Matrix mMatrix;
    private OnMatrixChangeListener mListener;
    private float[] mSrc = new float[4];
    private float[] mDst = new float[4];
    private int mCount;

    interface OnMatrixChangeListener {
        void onChange(Matrix matrix);
    }

    public MatrixGestureDetector(Matrix matrix, MatrixGestureDetector.OnMatrixChangeListener listener) {
        this.mMatrix = matrix;
        this.mListener = listener;
    }

    public void onTouchEvent(MotionEvent event) {
        if (event.getPointerCount() > 2) {
            return;
        }

        int action = event.getActionMasked();
        int index = event.getActionIndex();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_POINTER_DOWN:
                int idx = index * 2;
                mSrc[idx] = event.getX(index);
                mSrc[idx + 1] = event.getY(index);
                mCount++;
                ptpIdx = 0;
                break;

            case MotionEvent.ACTION_MOVE:
                for (int i = 0; i < mCount; i++) {
                    idx = ptpIdx + i * 2;
                    mDst[idx] = event.getX(i);
                    mDst[idx + 1] = event.getY(i);
                }
                mTempMatrix.setPolyToPoly(mSrc, ptpIdx, mDst, ptpIdx, mCount);
                mMatrix.postConcat(mTempMatrix);
                if(mListener != null) {
                    mListener.onChange(mMatrix);
                }
                System.arraycopy(mDst, 0, mSrc, 0, mDst.length);
                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                if (event.getPointerId(index) == 0) ptpIdx = 2;
                mCount--;
                break;
        }
    }
}

You have to redo it a little. Use your rectangle instead of bitmaps.