jack
jack

Reputation: 348

How to draw by finger on canvas after pinch to zoom coordinates changed in android?

Can any body have solution regarding how to draw on canvas using finger after zoom in and zoom out? I done pinch to zoom for canvas but stucking point is not draw right coordinate. I done more R&D from google. I have CustomView Class which i share on below.

public class DrawingView extends View {

    ArrayList<Path> pathList = new ArrayList<Path>();

    Rect mRect = new Rect();


    private static final int INVALID_POINTER_ID = -1;
     public Bitmap mMyChracter;
     private float mPosX;
     private float mPosY;

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

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

     private float focusX;
     private float focusY;

     private float lastFocusX = -1;
     private float lastFocusY = -1;

     static final int IMG_WIDTH = 640;
     static final int IMG_HEIGHT = 480;

     static final int IMAGE_X_POS = 560;
     static final int IMAGE_Y_POS = 20;

     boolean mFalgPath=false;

     boolean mFlagDrawAgain;

     float sy;
     float sx;
     public static Context context;
     Path path = new Path();
     MyPath p1 = new MyPath();
     MyPath p2 = new MyPath();

     Path mNewPath = new Path();
//   -------------------------------------


//   ...................................

        /*private final Bitmap bitmap;
        private final int width;
        private final int height;*/

        private Matrix transform = new Matrix();

        private Vector2D position = new Vector2D();
        private float scale = 1;
        private float angle = 0;

        private TouchManager touchManager = new TouchManager(2);
        private boolean isInitialized = false;

        // Debug helpers to draw lines between the two touch points
        private Vector2D vca = null;
        private Vector2D vcb = null;
        private Vector2D vpa = null;
        private Vector2D vpb = null;
        int mWidth;
        int mHeight;
//      ...............................

    private final Paint mDefaultPaint;

    private Paint mFillPaint;
    float x, y;
    private Canvas mLayerCanvas = new Canvas();
    private Bitmap mLayerBitmap;

    private Stack<DrawOp> mDrawOps = new Stack<>();
    private Stack<DrawOp> mUndoOps = new Stack<>();

    private SparseArray<DrawOp> mCurrentOps = new SparseArray<>(0);


//  For Drag and Pan zoom Code initialization
    private static float MIN_ZOOM = 1f;

    private static float MAX_ZOOM = 2f;
    private float scaleFactor = 1.f;

    private static ScaleGestureDetector detector; 

    boolean mFlagDrawing;

    private final Matrix mMatrix = new Matrix();

     int y_old=0,y_new=0;int zoomMode=0;
     float pinch_dist_old=0,pinch_dist_new=0;
     int zoomControllerScale=1;//new and old pinch distance to determine Zoom scale
        // These matrices will be used to move and zoom image
        Matrix matrix = new Matrix();
        Matrix savedMatrix = new Matrix();

        // Remember some things for zooming
        PointF start = new PointF();
        PointF mid = new PointF();
        float oldDist = 1f;

        // We can be in one of these 3 states
        static final int NONE = 0;
        static final int PAN = 1;
        static final int ZOOM = 2;
        int mode = NONE;

     private static final String TAG = "DebugTag";



//     New Code
     private Bitmap imgBitmap = null;

     private int containerWidth;
     private int containerHeight;

     Paint background;   

     //Matrices will be used to move and zoom image
//     Matrix matrix = new Matrix();
//     Matrix savedMatrix = new Matrix();

//     PointF start = new PointF();       

     float currentScale;
     float curX;
     float curY;

     //We can be in one of these 3 states
//     static final int NONE = 0;
//     static final int DRAG = 1;
//     static final int ZOOM = 2;
//     int mode = NONE;

     //For animating stuff   
     float targetX;
     float targetY;
     float targetScale;
     float targetScaleX;
     float targetScaleY;
     float scaleChange;
     float targetRatio;
     float transitionalRatio;

     float easing = 0.2f;   
     boolean isAnimating = false;

     float scaleDampingFactor = 0.5f;

     //For pinch and zoom
//     float oldDist = 1f;   
//     PointF mid = new PointF();

     private Handler mHandler = new Handler();       

     float minScale;
     float maxScale = 8.0f;

     float wpRadius = 25.0f;
     float wpInnerRadius = 20.0f;

     float screenDensity;

     private GestureDetector gestureDetector;

     public static final int DEFAULT_SCALE_FIT_INSIDE = 0;
     public static final int DEFAULT_SCALE_ORIGINAL = 1;

     private int defaultScale;


     private static final String EXTRA_EVENT_LIST = "event_list";
     private static final String EXTRA_STATE = "instance_state";
     private ArrayList<MotionEvent> eventList = new ArrayList<MotionEvent>(100);

    public DrawingView(Context context) {
        this(context, null, 0);
    }

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

    public DrawingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        mDefaultPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mDefaultPaint.setStyle(Paint.Style.STROKE);
        mDefaultPaint.setStrokeJoin(Paint.Join.ROUND);
        mDefaultPaint.setStrokeCap(Paint.Cap.ROUND);
        mDefaultPaint.setStrokeWidth(40);
        mDefaultPaint.setColor(Color.GREEN);

        /*mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mFillPaint.setStyle(Paint.Style.STROKE);
        mFillPaint.setStrokeJoin(Paint.Join.ROUND);
        mFillPaint.setStrokeCap(Paint.Cap.ROUND);
        mDefaultPaint.setStrokeWidth(40);
        mFillPaint.setColor(Color.GREEN);*/

        setFocusable(true);
        setFocusableInTouchMode(true);
        setBackgroundColor(Color.WHITE);

        setLayerType(LAYER_TYPE_SOFTWARE, null);
        setSaveEnabled(true);

//      Code for Zoom start
//      detector = new ScaleGestureDetector(getContext(), new ScaleListener());
//      Code for Zoom finish

        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    }


    private static float getDegreesFromRadians(float angle) {
        return (float)(angle * 180.0 / Math.PI);
    }


    // Single Touch Code
    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        /* float j = event.getX() / mScaleFactor + mRect.left;
         float k = event.getY() / mScaleFactor + mRect.top;*/

        final int pointerCount = MotionEventCompat.getPointerCount(event);

        switch (MotionEventCompat.getActionMasked(event)) {
        // switch(event.getAction()){
        // switch (event.getAction() & MotionEvent.ACTION_MASK) {

        case MotionEvent.ACTION_DOWN:
        // case MotionEventCompat.ACTION_POINTER_DOWN:
        {
            if (mFlagDrawing == true) {


                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish

    /*          float dx = event.getX();
                float dy = event.getY();

                if(mScaleFactor==1.f){
                    dx = event.getX() - mPosX;
                    dy = event.getY() - mPosY;
                }
//              tempPath = new Path();
                DrawOp current = new DrawOp(mDefaultPaint);
                current.getPath().moveTo(dx, dy);*/

            } else if (mFlagDrawing == false) {
                DrawOp currentNew = new DrawOp(mDefaultPaint);

                if(mFlagDrawAgain == true){
                    p1.moveTo(event.getX(), event.getY());
                /*  path.moveTo(event.getX(), event.getY());
                    mNewPath.addPath(path);*/
                }
                else{
                System.out.println("mFlagDrawing: " + mFlagDrawing);

                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    DrawOp current = new DrawOp(mDefaultPaint);
                    current.getPath().moveTo(event.getX(), event.getY());
//                  current.getPath().setLastPoint(dx, dy)
//                  mNewPath.addPath(current.getPath());
                    mCurrentOps.put(id, current);
                }
            }
            }
            // mFlagZoom = true;
            // }
        }
            break;

        case MotionEvent.ACTION_MOVE: {

            // for(int p = 0; p < pointerCount; p++){

            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish

/*               float dx = event.getX();
                    float dy = event.getY();;

                    if(mScaleFactor==1.f){
                        dx = event.getX() - mPosX;
                        dy = event.getY() - mPosY;
                    }
//                  mFalgPath = true; 
                    path.lineTo(dx, dy);*/

            } else if (mFlagDrawing == false) {
//              mFalgPath = true; 
//              Rect rect = new Rect();

                if(mFlagDrawAgain == true){
                        float dx = event.getX();
                        float dy = event.getY();

                        final int id = MotionEventCompat.getPointerId(event, 0);
                        DrawOp current = mCurrentOps.get(id);

                        if(mScaleFactor==1.f){
                            dx = event.getX() - mPosX;
                            dy = event.getY() - mPosY;
                        }

                        p1.lineTo(dx, dy);
                        /*p1.addPath(current.getPath());*/
                        /*path.lineTo(dx, dy);
                        mNewPath.addPath(path);*/
                }
                else{
                System.out.println("mFlagDrawing: " + mFlagDrawing);
                final int id = MotionEventCompat.getPointerId(event, 0);

                DrawOp current = mCurrentOps.get(id);

                final int historySize = event.getHistorySize();
                for (int h = 0; h < historySize; h++) {
                    x = event.getHistoricalX(h);
                    y = event.getHistoricalY(h);
                    current.getPath().lineTo(x, y);

//                  mNewPath.addPath(current.getPath());
//                  mNewPath.lineTo(x, y);
                }
                x = MotionEventCompat.getX(event, 0);
                y = MotionEventCompat.getY(event, 0);
                current.getPath().lineTo(x, y);

                }
            }
//              path.lineTo(x, y);
                // }
        }
            break;

        case MotionEvent.ACTION_UP:
            // case MotionEventCompat.ACTION_POINTER_UP:
            // {
            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish
                mActivePointerId = INVALID_POINTER_ID;

            } else if (mFlagDrawing == false) {
                System.out.println("mFlagDrawing: " + mFlagDrawing);
                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    mDrawOps.push(mCurrentOps.get(id));
                    mCurrentOps.remove(id);
                    // }
                    updateLayer();
                }
            }
            // }
            break;

        case MotionEvent.ACTION_CANCEL: {

            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish
                 mActivePointerId = INVALID_POINTER_ID;
            } else if (mFlagDrawing == false) {
                System.out.println("mFlagDrawing: " + mFlagDrawing);
                for (int p = 0; p < pointerCount; p++) {
                    mCurrentOps
                            .remove(MotionEventCompat.getPointerId(event, p));
                }
            }
            // mFlagZoom = true;
            // }
        }
            break;

        case MotionEvent.ACTION_POINTER_UP: {   

               final int pointerIndex = (event.getAction() &    MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
               final int pointerId = event.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 = event.getX(newPointerIndex) / mScaleFactor;
                mLastTouchY = event.getY(newPointerIndex) / mScaleFactor;
                mActivePointerId = event.getPointerId(newPointerIndex);
               }
               break;
              }
        default:
            return false;
        }

        invalidate();

        return true;
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldW, int oldH) {
        super.onSizeChanged(w, h, oldW, oldH);

        if(mLayerBitmap == null){
             mLayerBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
            }else{
                Bitmap temporary = Bitmap.createScaledBitmap(mLayerBitmap, w, h, true);
                mLayerBitmap = temporary;
            }
         mLayerCanvas = new Canvas(mLayerBitmap);

    }

    private void updateLayer() {
        mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
        for (DrawOp drawOp : mDrawOps) {
            if (drawOp != null) {
                drawOp.draw(mLayerCanvas);
            }
        }
        invalidate();
    }

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

        if (isInEditMode()) {
            return;
        }

        canvas.save();
        canvas.scale(mScaleFactor, mScaleFactor, focusX, focusY);
        canvas.translate(mPosX, mPosY);

        canvas.drawBitmap(mLayerBitmap, 0, 0, null);
        if(mScaleFactor!=1.f)
            canvas.restore();

         if(mScaleFactor==1.f)
            canvas.restore();

            if(mFlagDrawAgain == true){
//              canvas.drawPath(path, mDefaultPaint);

                canvas.drawPath(p1, mDefaultPaint);
            }
            else{
                 for (int i = 0; i < mCurrentOps.size(); i++) {

                        DrawOp current = mCurrentOps.valueAt(i);
                        if (current != null) {

                        current.draw(canvas);
                        }

                    }
            }


        canvas.restore();
    }

    public void operationClear() {
        path.reset();
        mDrawOps.clear();
        mUndoOps.clear();
        mCurrentOps.clear();
        updateLayer();
    }

    public void operationUndo() {
        if (mDrawOps.size() > 0) {
            mUndoOps.push(mDrawOps.pop());
            updateLayer();
        }
    }

    public void operationRedo() {
        if (mUndoOps.size() > 0) {
            mDrawOps.push(mUndoOps.pop());
            updateLayer();
        }
    }

    public void setPaintStrokeWidth(float widthPx) {
        mDefaultPaint.setStrokeWidth(widthPx);
    }

    public void setPaintOpacity(int percent) {
        int alphaValue = (int) Math.round(percent * (255.0 / 100.0));
        mDefaultPaint.setColor(combineAlpha(mDefaultPaint.getColor(),
                alphaValue));
    }

    public void setPaintColor(String color) {
        mDefaultPaint.setColor(combineAlpha(Color.parseColor(color), mDefaultPaint.getAlpha()));
    }

    public void setPaintColor(int color) {
        mDefaultPaint.setColor(combineAlpha(color, mDefaultPaint.getAlpha()));
    }

    public void setPaintMaskFilter(MaskFilter filter) {
        mDefaultPaint.setMaskFilter(filter);
    }

    public void setPaintShader(BitmapShader shader) {
        mDefaultPaint.setShader(shader);
    }

    public void setPaintColorFilter(ColorFilter colorFilter) {
        mDefaultPaint.setColorFilter(colorFilter);
    }

    private static int combineAlpha(int color, int alpha) {
        return (color & 0x00FFFFFF) | ((alpha & 0xFF) << 24);
    }

    private static class DrawOp {
        private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private final Path mPath = new Path();

        public DrawOp(Paint paint) {
            reset(paint);
        }

        void reset(Paint paint) {
            mPath.reset();
            update(paint);
        }

        void update(Paint paint) {
            mPaint.set(paint);
        }

        void draw(Canvas canvas) {
            canvas.drawPath(mPath, mPaint);
        }

        public Path getPath() {
            return mPath;
        }
    }
    public void fillShapeColor(){



    }

     private class ScaleListener extends
       ScaleGestureDetector.SimpleOnScaleGestureListener {
      @Override
      public boolean onScaleBegin(ScaleGestureDetector detector) {

       // float x = detector.getFocusX();
       // float y = detector.getFocusY();

       lastFocusX = -1;
       lastFocusY = -1;

       return true;
      }

      @Override
      public boolean onScale(ScaleGestureDetector detector) {
       mScaleFactor *= detector.getScaleFactor();

       focusX = detector.getFocusX();
       focusY = detector.getFocusY();


       if (lastFocusX == -1)
        lastFocusX = focusX;
       if (lastFocusY == -1)
        lastFocusY = focusY;

       mPosX += (focusX - lastFocusX);
       mPosY += (focusY - lastFocusY);
       Log.v("Hi Zoom", "Factor:"  + mScaleFactor);
       // Don't let the object get too small or too large.
       mScaleFactor = Math.max(1.f, Math.min(mScaleFactor, 2.0f));

       lastFocusX = focusX;
       lastFocusY = focusY;

       invalidate();
       return true;
      }
     }

    public void setDrawingFlag(boolean flag) {
//      System.out.println("Before Set mFlag " + mFlagDrawing);
        this.mFlagDrawing = flag;
//      System.out.println("After Set mFlag " + mFlagDrawing);
    }

    public void setDrawPath(boolean flag){
        this.mFalgPath = flag;
    }


    public void setDrawAgain(boolean flag){
        this.mFlagDrawAgain = flag;
    }

}

Upvotes: 2

Views: 1402

Answers (1)

jack
jack

Reputation: 348

After lots of research i found solution for moving actual canvas with finger and used ZoomControlButton for zoom in and zoom out canvas. I post my code here for someone help.

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

    if (isInEditMode()) {
        return;
    }

    if (mScaleFactor == 1.f)
    canvas.save();
    canvas.drawRGB(224, 224, 224);
    canvas.translate(-mPosX, -mPosY);
    canvas.drawBitmap(mLayerBitmap, new Matrix(), mDefaultPaint);

    Log.v("onDraw", "scale f : " + mScaleFactor);

    for (int i = 0; i < mCurrentOps.size(); i++) {
        Log.v("onDraw", "current ops");
        DrawOp current = mCurrentOps.valueAt(i);
        if (current != null) {
            current.draw(canvas);
        }
    }
    canvas.restore();
}

In onTouch() i used following code

@Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {

        final int pointerCount = MotionEventCompat.getPointerCount(event);

        switch (MotionEventCompat.getActionMasked(event)) {
        // switch(event.getAction()){
        // switch (event.getAction() & MotionEvent.ACTION_MASK) {

        case MotionEvent.ACTION_DOWN:
        // case MotionEventCompat.ACTION_POINTER_DOWN:
        {
            if (mFlagDrawing == true) {


                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish

            } else if (mFlagDrawing == false) {
                System.out.println("mFlagDrawing: " + mFlagDrawing);

                float dx = event.getX();
                float dy = event.getY();

                if(mScaleFactor==1.f){
                    dx = event.getX() - mPosX;
                    dy = event.getY() - mPosY;
                }

                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    DrawOp current = new DrawOp(mDefaultPaint);
                    current.getPath().moveTo(event.getX() + mPosX, event.getY() + mPosY);
                    mCurrentOps.put(id, current);
                }
            }
        }
            break;

        case MotionEvent.ACTION_MOVE: {

            // for(int p = 0; p < pointerCount; p++){

            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish

            } else if (mFlagDrawing == false) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                final int id = MotionEventCompat.getPointerId(event, 0);

                DrawOp current = mCurrentOps.get(id);

                final int historySize = event.getHistorySize();
                for (int h = 0; h < historySize; h++) {
                    x = event.getHistoricalX(h) + mPosX;
                    y = event.getHistoricalY(h) + mPosY;
                    current.getPath().lineTo(x, y);
                }
                x = MotionEventCompat.getX(event, 0) + mPosX;
                y = MotionEventCompat.getY(event, 0) + mPosY;
                current.getPath().lineTo(x, y);

                }
            }

            break;

        case MotionEvent.ACTION_UP:
            // case MotionEventCompat.ACTION_POINTER_UP:
            // {
            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish
                mActivePointerId = INVALID_POINTER_ID;

            } else if (mFlagDrawing == false) {
                System.out.println("mFlagDrawing: " + mFlagDrawing);
                for (int p = 0; p < pointerCount; p++) {
                    final int id = MotionEventCompat.getPointerId(event, p);
                    mDrawOps.push(mCurrentOps.get(id));
                    mCurrentOps.remove(id);
                    // }
                    updateLayer();
                }
            }
            // }
            break;

        case MotionEvent.ACTION_CANCEL: {

            if (mFlagDrawing == true) {

                System.out.println("mFlagDrawing: " + mFlagDrawing);
                // Code for Zoom start
                // detector.onTouchEvent(event);
                mScaleDetector.onTouchEvent(event);
                // Code for Zoom finish
                 mActivePointerId = INVALID_POINTER_ID;
            } else if (mFlagDrawing == false) {
                System.out.println("mFlagDrawing: " + mFlagDrawing);
                for (int p = 0; p < pointerCount; p++) {
                    mCurrentOps
                            .remove(MotionEventCompat.getPointerId(event, p));
                }
            }
            // mFlagZoom = true;
            // }
        }
            break;

        default:
            return false;
        }

        invalidate();

        return true;
    }

When updating the whole Layer then i used following syntax:

private void updateLayer() {
        mLayerCanvas.drawColor(0, PorterDuff.Mode.CLEAR);

        mLayerCanvas.drawRGB(255,  255,  255);


        mLayerCanvas.save();

        for (DrawOp drawOp : mDrawOps) {
            if (drawOp != null) {
                drawOp.draw(mLayerCanvas);
            }
        }
        mLayerCanvas.restore();
        invalidate();
    }

Upvotes: 1

Related Questions