Pan, Zoom and Scale a custom View for Canvas drawing in Android

偶尔善良 提交于 2019-12-23 03:22:40

问题


I'm working on a drawing app, where the user can pan & zoom to a specific portion of the screen and start drawing with zoom applied. To be more specific, I'm looking for a way to implement zoom, pinch & pan gesture in Canvas (horizontal and vertical scrolling with moveable x, y coordinates).

Right now, I've successfully developed the zoom only feature but it's not accurate and the pan option is not working.

See my sample code here,

       public class DrawingView extends View {

        //canvas
        private Canvas drawCanvas;
        //canvas bitmap
        private Bitmap canvasBitmap;

        private boolean dragged = false;
        private float displayWidth;
        private float displayHeight;

        //These two constants specify the minimum and maximum zoom
        private static float MIN_ZOOM = 1f;
        private static float MAX_ZOOM = 5f;

        private float scaleFactor = 1.f;
        private ScaleGestureDetector detector;

        //These constants specify the mode that we're in
        private static int NONE = 0;
        private static int DRAG = 1;
        private static int ZOOM = 2;

        private int mode;

         public DrawingView(Context context, AttributeSet attrs) {
                super(context, attrs);
                ctx = context;
                setupDrawing();
            }

         private void setupDrawing() {
                detector = new ScaleGestureDetector(getContext(), new ScaleListener());

                WindowManager wm = (WindowManager) ctx.getSystemService(Context.WINDOW_SERVICE);
                Display display = wm.getDefaultDisplay();
                DisplayMetrics metrics = new DisplayMetrics();
                display.getMetrics(metrics);

                displayWidth = metrics.widthPixels;
                displayHeight = metrics.heightPixels;

                setFocusable(true);
                setFocusableInTouchMode(true);
        }

class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
            return true;
        }
    }

        @Override
            protected void onSizeChanged(int w, int h, int oldw, int oldh) {
                //view given size
                super.onSizeChanged(w, h, oldw, oldh);
                canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                drawCanvas = new Canvas(canvasBitmap);
            }

         @Override
            public boolean onTouchEvent(MotionEvent event) {

                    //detect user touch
                    float x = event.getX();
                    float y = event.getY();

                    switch (event.getAction() & MotionEvent.ACTION_MASK) {

                        case MotionEvent.ACTION_DOWN:
                            //
                            downx = event.getX();
                            downy = event.getY();

                            mode = DRAG;
                            //We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
                            //amount for each coordinates This works even when we are translating the first time because the initial
                            //values for these two variables is zero.
                            startX = event.getX() - previousTranslateX;
                            startY = event.getY() - previousTranslateY;
                            break;

                            case MotionEvent.ACTION_MOVE:
                            translateX = event.getX() - startX;
                            translateY = event.getY() - startY;
                            //We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
                            //This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
                            double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) + Math.pow(event.getY() - (startY + previousTranslateY), 2));
                            if (distance > 0) {
                                dragged = true;
                            }
                            break;

                            case MotionEvent.ACTION_POINTER_DOWN:
                            mode = ZOOM;
                            break;


                            ase MotionEvent.ACTION_UP:
                            //
                            upx = event.getX();
                            upy = event.getY();

                            mode = NONE;
                            dragged = false;
                            //All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
                            //previousTranslate
                            previousTranslateX = translateX;
                            previousTranslateY = translateY;
                            break;

                            case MotionEvent.ACTION_POINTER_UP:
                            mode = DRAG;
                            //This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
                            //and previousTranslateY when the second finger goes up
                            previousTranslateX = translateX;
                            previousTranslateY = translateY;
                            break;

                            detector.onTouchEvent(event);

                    //We redraw the canvas only in the following cases:
                    //
                    // o The mode is ZOOM
                    //        OR
                    // o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
                    //   set to true (meaning the finger has actually moved)
                    if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {
                        invalidate();
                    }
        }

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

                //If translateX times -1 is lesser than zero, let's set it to zero. This takes care of the left bound
                if ((translateX * -1) < 0) {
                    translateX = 0;
                }

                //This is where we take care of the right bound. We compare translateX times -1 to (scaleFactor - 1) * displayWidth.
                //If translateX is greater than that value, then we know that we've gone over the bound. So we set the value of
                //translateX to (1 - scaleFactor) times the display width. Notice that the terms are interchanged; it's the same
                //as doing -1 * (scaleFactor - 1) * displayWidth
                else if ((translateX * -1) > (scaleFactor - 1) * displayWidth) {
                    translateX = (1 - scaleFactor) * displayWidth;
                }

                if (translateY * -1 < 0) {
                    translateY = 0;
                }

                //We do the exact same thing for the bottom bound, except in this case we use the height of the display
                else if ((translateY * -1) > (scaleFactor - 1) * displayHeight) {
                    translateY = (1 - scaleFactor) * displayHeight;
                }

                //We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
                //because the translation amount also gets scaled according to how much we've zoomed into the canvas.
                canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
            }

            //We're going to scale the X and Y coordinates by the same amount
            //canvas.scale(scaleFactor, scaleFactor);
            canvas.scale(this.scaleFactor, this.scaleFactor, this.detector.getFocusX(), this.detector.getFocusY());
    }

来源:https://stackoverflow.com/questions/39155545/pan-zoom-and-scale-a-custom-view-for-canvas-drawing-in-android

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!