Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android zoom, drag, draw on ImageView

I have a problem with drawing and zooming ImageView. Help me, please..

When I draw something and then drag or zoom an image - the drawing remains on its place, as you can see on the screencapture. And I need simply to draw on picture, and have possibility to zoom and drag this picture.

enter image description here enter image description here

I have a custom ImageView with this actions:

 public class DrawingView extends ImageView {
// onTouch
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;

private Path mPath;

private boolean zoomEnabled = true;

private Paint drawPaint, canvasPaint;
//initial color
private int paintColor = 0xFF660000;
//canvas
private Canvas drawCanvas;
//canvas bitmap
private Bitmap canvasBitmap;

private static final int INVALID_POINTER_ID = -1;

private float mPosX;
private float mPosY;

private float mLastTouchX;
private float mLastTouchY;
private float mLastGestureX;
private float mLastGestureY;
private int mActivePointerId = INVALID_POINTER_ID;

private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;

public DrawingView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
    setupDrawing();
}

public DrawingView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    setupDrawing();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {

    super.onSizeChanged(w, h, oldw, oldh);
    canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
    drawCanvas = new Canvas(canvasBitmap);

}

@Override
public boolean onTouchEvent(MotionEvent ev) {
    final int action = ev.getAction();
    mScaleDetector.onTouchEvent(ev);
    if (zoomEnabled) {
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScaleDetector.isInProgress()) {
                    final float x = ev.getX();
                    final float y = ev.getY();

                    mLastTouchX = x;
                    mLastTouchY = y;
                    mActivePointerId = ev.getPointerId(0);
                }
                break;
            }
            case MotionEvent.ACTION_POINTER_1_DOWN: {
                if (mScaleDetector.isInProgress()) {
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();
                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {

                // Only move if the ScaleGestureDetector isn't processing a gesture.
                if (!mScaleDetector.isInProgress()) {
                    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                    final float x = ev.getX(pointerIndex);
                    final float y = ev.getY(pointerIndex);

                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;

                    mPosX += dx;
                    mPosY += dy;

                    invalidate();

                    mLastTouchX = x;
                    mLastTouchY = y;
                } else {
                    final float gx = mScaleDetector.getFocusX();
                    final float gy = mScaleDetector.getFocusY();

                    final float gdx = gx - mLastGestureX;
                    final float gdy = gy - mLastGestureY;

                    mPosX += gdx;
                    mPosY += gdy;

                    invalidate();

                    mLastGestureX = gx;
                    mLastGestureY = gy;
                }

                break;
            }
            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
            case MotionEvent.ACTION_POINTER_UP: {

                final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
                final int pointerId = ev.getPointerId(pointerIndex);
                if (pointerId == mActivePointerId) {
                    // This was our active pointer going up. Choose a new
                    // active pointer and adjust accordingly.
                    final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                    mLastTouchX = ev.getX(newPointerIndex);
                    mLastTouchY = ev.getY(newPointerIndex);
                    mActivePointerId = ev.getPointerId(newPointerIndex);
                } else {
                    final int tempPointerIndex = ev.findPointerIndex(mActivePointerId);
                    mLastTouchX = ev.getX(tempPointerIndex);
                    mLastTouchY = ev.getY(tempPointerIndex);
                }
                break;
            }
        }

        return true;
    } else {
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                touchStart(ev.getX(), ev.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touchMove(ev.getX(), ev.getY());
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touchUp();
                invalidate();
                break;
        }
        return true;
    }
}

@Override
public void onDraw(Canvas canvas) {
    canvas.save();
    canvas.translate(mPosX, mPosY);

    if (mScaleDetector.isInProgress()) {
        canvas.scale(mScaleFactor, mScaleFactor, mScaleDetector.getFocusX(), mScaleDetector.getFocusY());
    }
    else{
        canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY);
    }
    super.onDraw(canvas);
    canvas.restore();

    canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
    canvas.drawPath(mPath, drawPaint);
}

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        mScaleFactor *= detector.getScaleFactor();

        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));

        invalidate();
        return true;
    }
}

private void setupDrawing(){
    mPath = new Path();
    drawPaint = new Paint();
    drawPaint.setColor(paintColor);
    drawPaint.setAntiAlias(true);
    drawPaint.setStrokeWidth(20);
    drawPaint.setStyle(Paint.Style.STROKE);
    drawPaint.setStrokeJoin(Paint.Join.ROUND);
    drawPaint.setStrokeCap(Paint.Cap.ROUND);
    canvasPaint = new Paint(Paint.DITHER_FLAG);
}

public void setZoomEnabled(boolean b){
    this.zoomEnabled = b;
}

private void touchStart(float x, float y) {
    // mPath.reset();
    mPath.moveTo(x, y);
    mX = x;
    mY = y;
}

private void touchMove(float x, float y) {
    float dx = Math.abs(x - mX);
    float dy = Math.abs(y - mY);
    if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
        mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
        mX = x;
        mY = y;
    }
}

private void touchUp() {
    mPath.lineTo(mX, mY);
    // commit the path to our offscreen
    drawCanvas.drawPath(mPath, drawPaint);
    // kill this so we don't double draw
    mPath.reset();
}

And activity:

 public class DrawActivity extends Activity{

DrawingView image;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.draw);

    final Paint paint = new Paint();
    paint.setColor(MyApp.getColorRes(R.color.primary));
    paint.setStrokeWidth(10);

    image = (DrawingView) findViewById(R.id.image);

    Bitmap bmp = BitmapFactory.decodeByteArray(
            getIntent().getByteArrayExtra("byteArray"), 0, getIntent().getByteArrayExtra("byteArray").length);


    Bitmap alteredBmp = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
    final Canvas canvas = new Canvas(alteredBmp);
    canvas.drawBitmap(bmp, 0, 0, paint);
    image.setImageBitmap(alteredBmp);

    ImageButton paintBtn = (ImageButton) findViewById(R.id.paint_btn);
    paintBtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View button) {
            button.setSelected(!button.isSelected());
            image.setZoomEnabled(!button.isSelected());
        }
    });

}
like image 524
Paul Chernenko Avatar asked Nov 18 '15 21:11

Paul Chernenko


1 Answers

In your onDraw(), you are drawing your path directly using the View's canvas. Instead draw the path on to the canvasBitmap via drawCanvas. Once this is done, your canvasBitmap will contain both the image and the drawing. Now when you scale or move, both will change appropriately, since they are now in a single bitmap.

Instead of the this:

canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(mPath, drawPaint);

Do something like this:

drawCanvas.drawPath(mPath, drawPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
like image 151
Henry Avatar answered Sep 27 '22 20:09

Henry