0

I am trying to make a painting app. Zoom kinda works, when i try to draw on canvas after the zoom, it does not draw on the location of the finger. instead it draws scale to the device screen.

I did some research but could not find the answer to the solution. -> float x = (event.getX() - scalePointX)/mScaleFactor; // does not work,

I tried to implement matrix but was unsuccessful. Can someone please help me to draw on the finger when the canvas is zoomed in or out?

Thank you!

public class paintView extends View {

    Paint paint;
    Path path;
    Bitmap bitmap;
    Canvas mcanvas;
    private final float TOUCH_TOLERANCE = 4;
    private float mX, mY;
    public static final int DEFAULT_BG_COLOR = Color.WHITE;
    private int backgroundColor = DEFAULT_BG_COLOR;
    private ArrayList<FingerPath> paths = new ArrayList<>();
    private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);

    private final ScaleGestureDetector mScaleGesture;
    private float mScaleFactor = 1.f;
    private float mPosX;
    private float mPosY;
    private  float scalePointX, scalePointY;


    private Rect clipBounds_canvas;

    public paintView(Context context) {
        this(context, null);
    }

    public paintView(Context context, AttributeSet attrs){
        super(context, attrs);

        mScaleGesture = new ScaleGestureDetector(context, new ScaleListener());

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(8f);

    }

    public void init(DisplayMetrics metrics){
        int height = metrics.heightPixels;
        int width = metrics.widthPixels;

        bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        mcanvas = new Canvas(bitmap);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();
        mcanvas.drawColor(backgroundColor);

        clipBounds_canvas = canvas.getClipBounds();

        for(FingerPath fp: paths){
            paint.setMaskFilter(null);

            mcanvas.drawPath(fp.path, paint);
        }
        canvas.translate(mPosX, mPosY);
        canvas.scale(mScaleFactor, mScaleFactor,scalePointX,scalePointY);

        canvas.drawBitmap(bitmap, 0, 0, mBitmapPaint);
        canvas.restore();

    }

    private void touchStart(float x, float y){
        path = new Path();
        FingerPath fp = new FingerPath(path);
        paths.add(fp);

        path.reset();
        path.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 ){
            path.quadTo(mX, mY, (x+mX)/2, (y+mY)/2);
            mX = x;
            mY = y;
        }
    }

    private void touchUp(){
        path.lineTo(mX,mY);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final float x = (event.getX() - scalePointX)/mScaleFactor;
        final float y = (event.getY() - scalePointY)/mScaleFactor;

        mScaleGesture.onTouchEvent(event);
        final int action = event.getAction();

        switch(action & MotionEvent.ACTION_MASK){
            case MotionEvent.ACTION_DOWN:
                touchStart(x, y);
                invalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                touchMove(x,y);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                touchUp();
                invalidate();
                break;
        }
        return true;
    }

    public class FingerPath {

        public Path path;

        public FingerPath(Path path) {
            this.path = path;
        }
    }
    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            mScaleFactor *= detector.getScaleFactor();
            scalePointX = detector.getFocusX();
            scalePointY = detector.getFocusY();
            // Don't let the object get too small or too large.
            mScaleFactor = Math.max(0.5f, Math.min(mScaleFactor, 3.0f));

            //zoom out 'up to' the size of canvas(screen size)
            //mScaleFactor = (mScaleFactor < 1 ? 1 : mScaleFactor);

            invalidate();
            return true;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }

    }
}
jun
  • 23
  • 3

1 Answers1

0

I had that problem and was able to solve it with This stack overflow question. What I did was store the current scale offset and when I draw the new paths, I'd offset the drawing matrix by this stored value. Your solution is really close, instead of scaling the canvas you can scale the drawing matrix. Also a friendly tip, you might need to scale the line thickness after zooming also, so use the same value that was stored to scale.

thatGuy
  • 122
  • 1
  • 10