How to draw by finger on canvas after pinch to zoo

2019-05-18 09:19发布

问题:

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;
    }

}

回答1:

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();
    }