View with horizontal and vertical pan/drag and pinch-zoom

前端 未结 9 2023
离开以前
离开以前 2020-11-28 22:56

Is it possible to have a view who supports horizontal and vertical pan/drag. On top of that, I want to be able to pinch to zoom and double tap to zoom. Does this view exists

相关标签:
9条回答
  • 2020-11-28 23:33

    Repost of @Artjom answer with minor errors fixed, namely braces, imports, and extending ViewGroup.

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Rect;
    import android.view.*;
    
    public class ZoomableViewGroup extends ViewGroup {
    
        private static final int INVALID_POINTER_ID = 1;
        private int mActivePointerId = INVALID_POINTER_ID;
    
        private float mScaleFactor = 1;
        private ScaleGestureDetector mScaleDetector;
        private Matrix mScaleMatrix = new Matrix();
        private Matrix mScaleMatrixInverse = new Matrix();
    
        private float mPosX;
        private float mPosY;
        private Matrix mTranslateMatrix = new Matrix();
        private Matrix mTranslateMatrixInverse = new Matrix();
    
        private float mLastTouchX;
        private float mLastTouchY;
    
        private float mFocusY;
    
        private float mFocusX;
    
        private float[] mInvalidateWorkingArray = new float[6];
        private float[] mDispatchTouchEventWorkingArray = new float[2];
        private float[] mOnTouchEventWorkingArray = new float[2];
    
    
        public ZoomableViewGroup(Context context) {
            super(context);
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
            mTranslateMatrix.setTranslate(0, 0);
            mScaleMatrix.setScale(1, 1);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight());
                }
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    measureChild(child, widthMeasureSpec, heightMeasureSpec);
                }
            }
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            canvas.save();
            canvas.translate(mPosX, mPosY);
            canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
            super.dispatchDraw(canvas);
            canvas.restore();
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            mDispatchTouchEventWorkingArray[0] = ev.getX();
            mDispatchTouchEventWorkingArray[1] = ev.getY();
            mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
            ev.setLocation(mDispatchTouchEventWorkingArray[0],
                    mDispatchTouchEventWorkingArray[1]);
            return super.dispatchTouchEvent(ev);
        }
    
        /**
         * Although the docs say that you shouldn't override this, I decided to do
         * so because it offers me an easy way to change the invalidated area to my
         * likening.
         */
        @Override
        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    
            mInvalidateWorkingArray[0] = dirty.left;
            mInvalidateWorkingArray[1] = dirty.top;
            mInvalidateWorkingArray[2] = dirty.right;
            mInvalidateWorkingArray[3] = dirty.bottom;
    
    
            mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
            dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]),
                    Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3]));
    
            location[0] *= mScaleFactor;
            location[1] *= mScaleFactor;
            return super.invalidateChildInParent(location, dirty);
        }
    
        private float[] scaledPointsToScreenPoints(float[] a) {
            mScaleMatrix.mapPoints(a);
            mTranslateMatrix.mapPoints(a);
            return a;
        }
    
        private float[] screenPointsToScaledPoints(float[] a){
            mTranslateMatrixInverse.mapPoints(a);
            mScaleMatrixInverse.mapPoints(a);
            return a;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            mOnTouchEventWorkingArray[0] = ev.getX();
            mOnTouchEventWorkingArray[1] = ev.getY();
    
            mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
    
            ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
            mScaleDetector.onTouchEvent(ev);
    
            final int action = ev.getAction();
            switch (action & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    final float x = ev.getX();
                    final float y = ev.getY();
    
                    mLastTouchX = x;
                    mLastTouchY = y;
    
                    // Save the ID of this pointer
                    mActivePointerId = ev.getPointerId(0);
                    break;
                }
    
                case MotionEvent.ACTION_MOVE: {
                    // Find the index of the active pointer and fetch its position
                    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;
                    mTranslateMatrix.preTranslate(dx, dy);
                    mTranslateMatrix.invert(mTranslateMatrixInverse);
    
                    mLastTouchX = x;
                    mLastTouchY = y;
    
                    invalidate();
                    break;
                }
    
                case MotionEvent.ACTION_UP: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }
    
                case MotionEvent.ACTION_CANCEL: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }
    
                case MotionEvent.ACTION_POINTER_UP: {
                    // Extract the index of the pointer that left the touch sensor
                    final int pointerIndex = (action & 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);
                    }
                    break;
                }
            }
            return true;
        }
    
        private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                mScaleFactor *= detector.getScaleFactor();
                if (detector.isInProgress()) {
                    mFocusX = detector.getFocusX();
                    mFocusY = detector.getFocusY();
                }
                mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
                mScaleMatrix.setScale(mScaleFactor, mScaleFactor,
                        mFocusX, mFocusY);
                mScaleMatrix.invert(mScaleMatrixInverse);
                invalidate();
                requestLayout();
    
    
                return true;
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-28 23:43

    I think it's possible to achieve what you want, but there is, as far as I know build in solution for it. From the second part of your question I guess that you don't want a zoomable View but a ViewGroup which is the super class of all Views that can contain other view (e.g. Layouts). Here is some code you could start from building your own ViewGroup most of it comes from this blog post:

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Matrix;
    import android.graphics.Rect;
    import android.view.*;
    
    public class ZoomableViewGroup extends ViewGroup {
    
        private static final int INVALID_POINTER_ID = 1;
        private int mActivePointerId = INVALID_POINTER_ID;
    
        private float mScaleFactor = 1;
        private ScaleGestureDetector mScaleDetector;
        private Matrix mScaleMatrix = new Matrix();
        private Matrix mScaleMatrixInverse = new Matrix();
    
        private float mPosX;
        private float mPosY;
        private Matrix mTranslateMatrix = new Matrix();
        private Matrix mTranslateMatrixInverse = new Matrix();
    
        private float mLastTouchX;
        private float mLastTouchY;
    
        private float mFocusY;
    
        private float mFocusX;
    
        private float[] mInvalidateWorkingArray = new float[6];
        private float[] mDispatchTouchEventWorkingArray = new float[2];
        private float[] mOnTouchEventWorkingArray = new float[2];
    
    
        public ZoomableViewGroup(Context context) {
            super(context);
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
            mTranslateMatrix.setTranslate(0, 0);
            mScaleMatrix.setScale(1, 1);
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    child.layout(l, t, l+child.getMeasuredWidth(), t + child.getMeasuredHeight());
                }
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    measureChild(child, widthMeasureSpec, heightMeasureSpec);
                }
            }
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
            canvas.save();
            canvas.translate(mPosX, mPosY);
            canvas.scale(mScaleFactor, mScaleFactor, mFocusX, mFocusY);
            super.dispatchDraw(canvas);
            canvas.restore();
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            mDispatchTouchEventWorkingArray[0] = ev.getX();
            mDispatchTouchEventWorkingArray[1] = ev.getY();
            mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
            ev.setLocation(mDispatchTouchEventWorkingArray[0],
                    mDispatchTouchEventWorkingArray[1]);
            return super.dispatchTouchEvent(ev);
        }
    
        /**
         * Although the docs say that you shouldn't override this, I decided to do
         * so because it offers me an easy way to change the invalidated area to my
         * likening.
         */
        @Override
        public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    
            mInvalidateWorkingArray[0] = dirty.left;
            mInvalidateWorkingArray[1] = dirty.top;
            mInvalidateWorkingArray[2] = dirty.right;
            mInvalidateWorkingArray[3] = dirty.bottom;
    
    
            mInvalidateWorkingArray = scaledPointsToScreenPoints(mInvalidateWorkingArray);
            dirty.set(Math.round(mInvalidateWorkingArray[0]), Math.round(mInvalidateWorkingArray[1]),
                    Math.round(mInvalidateWorkingArray[2]), Math.round(mInvalidateWorkingArray[3]));
    
            location[0] *= mScaleFactor;
            location[1] *= mScaleFactor;
            return super.invalidateChildInParent(location, dirty);
        }
    
        private float[] scaledPointsToScreenPoints(float[] a) {
            mScaleMatrix.mapPoints(a);
            mTranslateMatrix.mapPoints(a);
            return a;
        }
    
        private float[] screenPointsToScaledPoints(float[] a){
            mTranslateMatrixInverse.mapPoints(a);
            mScaleMatrixInverse.mapPoints(a);
            return a;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            mOnTouchEventWorkingArray[0] = ev.getX();
            mOnTouchEventWorkingArray[1] = ev.getY();
    
            mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
    
            ev.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
            mScaleDetector.onTouchEvent(ev);
    
            final int action = ev.getAction();
            switch (action & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN: {
                    final float x = ev.getX();
                    final float y = ev.getY();
    
                    mLastTouchX = x;
                    mLastTouchY = y;
    
                    // Save the ID of this pointer
                    mActivePointerId = ev.getPointerId(0);
                    break;
                }
    
                case MotionEvent.ACTION_MOVE: {
                    // Find the index of the active pointer and fetch its position
                    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;
                    mTranslateMatrix.preTranslate(dx, dy);
                    mTranslateMatrix.invert(mTranslateMatrixInverse);
    
                    mLastTouchX = x;
                    mLastTouchY = y;
    
                    invalidate();
                    break;
                }
    
                case MotionEvent.ACTION_UP: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }
    
                case MotionEvent.ACTION_CANCEL: {
                    mActivePointerId = INVALID_POINTER_ID;
                    break;
                }
    
                case MotionEvent.ACTION_POINTER_UP: {
                    // Extract the index of the pointer that left the touch sensor
                    final int pointerIndex = (action & 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);
                    }
                    break;
                }
            }
            return true;
        }
    
        private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                mScaleFactor *= detector.getScaleFactor();
                if (detector.isInProgress()) {
                    mFocusX = detector.getFocusX();
                    mFocusY = detector.getFocusY();
                }
                mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
                mScaleMatrix.setScale(mScaleFactor, mScaleFactor,
                        mFocusX, mFocusY);
                mScaleMatrix.invert(mScaleMatrixInverse);
                invalidate();
                requestLayout();
    
    
                return true;
            }
        }
    }
    

    What this class should be able to do, is dragging the content around and allowing pinch to zoom, double tap to zoom isn't possible right now, but it should be easy to implement in the onTouchEvent method.

    If you have questions how to layout the childs in your ViewGroup I found this video very helpfull or if you have any further questions how single methods work or anything else feel free to ask in the comments.

    0 讨论(0)
  • 2020-11-28 23:46

    I came with this solution (A mix of your codes and some of my ideas) :

    • double tap zoom and unzoom
    • zoom and unzoom afterdouble tap (with one finger)
    • pan around ok
    • pinch zoom ok and it zooms where you point
    • child views are touchable
    • layout with border ! (Cant get off the layout by unzooming or by panning around)

    its not animated but fullyworking. enjoy

    Usage :

    <com.yourapppath.ZoomableViewGroup
        android:id="@+id/zoomControl"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <FrameLayout
            android:id="@+id/frameLayoutZoom"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <ImageView
                android:id="@+id/planImageView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/yourdrawable"
                android:scaleType="center" />
    
        </FrameLayout>
    </com.yourapppath.ZoomableViewGroup>
    

    and here is the zoomableViewGroup Java file, just copy and use :

    public class ZoomableViewGroup extends ViewGroup {
    
        private boolean doubleTap = false;
    
    
        private float MIN_ZOOM = 1f;
        private float MAX_ZOOM = 2.5f;
        private float[] topLeftCorner = {0, 0};
        private float scaleFactor;
    
        // States.
        private static final byte NONE = 0;
        private static final byte DRAG = 1;
        private static final byte ZOOM = 2;
    
        private byte mode = NONE;
    
        // Matrices used to move and zoom image.
        private Matrix matrix = new Matrix();
        private Matrix matrixInverse = new Matrix();
        private Matrix savedMatrix = new Matrix();
    
        // Parameters for zooming.
        private PointF start = new PointF();
        private PointF mid = new PointF();
        private float oldDist = 1f;
        private float[] lastEvent = null;
        private long lastDownTime = 0l;
        private long downTime = 0l;
    
        private float[] mDispatchTouchEventWorkingArray = new float[2];
        private float[] mOnTouchEventWorkingArray = new float[2];
    
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            mDispatchTouchEventWorkingArray[0] = ev.getX();
            mDispatchTouchEventWorkingArray[1] = ev.getY();
            mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
            ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
            return super.dispatchTouchEvent(ev);
        }
    
        public ZoomableViewGroup(Context context) {
            super(context);
        }
    
        public ZoomableViewGroup(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public ZoomableViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
    
        /**
         * Determine the space between the first two fingers
         */
        private float spacing(MotionEvent event) {
            float x = event.getX(0) - event.getX(1);
            float y = event.getY(0) - event.getY(1);
            return (float) Math.sqrt(x * x + y * y);
        }
    
        /**
         * Calculate the mid point of the first two fingers
         */
        private void midPoint(PointF point, MotionEvent event) {
            float x = event.getX(0) + event.getX(1);
            float y = event.getY(0) + event.getY(1);
            point.set(x / 2, y / 2);
        }
    
        private float[] scaledPointsToScreenPoints(float[] a) {
            matrix.mapPoints(a);
            return a;
        }
    
        private float[] screenPointsToScaledPoints(float[] a) {
            matrixInverse.mapPoints(a);
            return a;
        }
    
    
        @Override
        public void onLayout(boolean changed, int left, int top, int right, int bottom) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
                }
            }
        }
    
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
           super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                if (child.getVisibility() != GONE) {
                    measureChild(child, widthMeasureSpec, heightMeasureSpec);
                }
            }
        }
    
        @Override
        public void dispatchDraw(Canvas canvas) {
            float[] values = new float[9];
            matrix.getValues(values);
            canvas.save();
            canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
            canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
            topLeftCorner[0] = values[Matrix.MTRANS_X];
            topLeftCorner[1] = values[Matrix.MTRANS_Y];
            scaleFactor = values[Matrix.MSCALE_X];
            super.dispatchDraw(canvas);
            canvas.restore();
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // handle touch events here
            mOnTouchEventWorkingArray[0] = event.getX();
            mOnTouchEventWorkingArray[1] = event.getY();
    
            mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
    
            event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
    
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    savedMatrix.set(matrix);
                    mode = DRAG;
                    lastEvent = null;
                    downTime = SystemClock.elapsedRealtime();
                    if (downTime - lastDownTime < 250l) {
                        doubleTap = true;
                        float density = getResources().getDisplayMetrics().density;
                        if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
                            savedMatrix.set(matrix);                                                         //repetition of savedMatrix.setmatrix
                            mid.set(event.getX(), event.getY());
                            mode = ZOOM;
                            lastEvent = new float[4];
                            lastEvent[0] = lastEvent[1] = event.getX();
                            lastEvent[2] = lastEvent[3] = event.getY();
                        }    
                        lastDownTime = 0l;
                    } else {
                        doubleTap = false;
                        lastDownTime = downTime;
                    }
                    start.set(event.getX(), event.getY());
    
                    break;
                case MotionEvent.ACTION_POINTER_DOWN:
                    oldDist = spacing(event);
                    if (oldDist > 10f) {
                        savedMatrix.set(matrix);
                        midPoint(mid, event);
                        mode = ZOOM;
                    }
                    lastEvent = new float[4];
                    lastEvent[0] = event.getX(0);
                    lastEvent[1] = event.getX(1);
                    lastEvent[2] = event.getY(0);
                    lastEvent[3] = event.getY(1);
                    break;
                case MotionEvent.ACTION_UP:
    
    
                    if (doubleTap && scaleFactor < 1.8f){
                        matrix.postScale(2.5f/scaleFactor, 2.5f/scaleFactor, mid.x, mid.y);
                    } else if(doubleTap && scaleFactor >= 1.8f){
                        matrix.postScale(1.0f/scaleFactor, 1.0f/scaleFactor, mid.x, mid.y);
                    }
    
                    Handler handler = new Handler();
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if(topLeftCorner[0] >= 0){
                                matrix.postTranslate(-topLeftCorner[0],0);
                            } else if (topLeftCorner[0] < -getWidth()*(scaleFactor-1)){
                                matrix.postTranslate((-topLeftCorner[0]) - getWidth()*(scaleFactor-1) ,0);
                            }
                            if(topLeftCorner[1] >= 0){
                                matrix.postTranslate(0,-topLeftCorner[1]);
                            } else if (topLeftCorner[1] < -getHeight()*(scaleFactor-1)){
                                matrix.postTranslate(0,(-topLeftCorner[1]) - getHeight()*(scaleFactor-1));
                            }
                            matrix.invert(matrixInverse);
                            invalidate();
                        }
                    }, 1);
    
                    break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    mode = NONE;
                    lastEvent = null;
                    break;
                case MotionEvent.ACTION_MOVE:
    
                    final float density = getResources().getDisplayMetrics().density;
                    if (mode == DRAG) {
                        matrix.set(savedMatrix);
                        float dx = event.getX() - start.x;
                        float dy = event.getY() - start.y;
                        matrix.postTranslate(dx, dy);
                        matrix.invert(matrixInverse);
                        if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
                            lastDownTime = 0l;
                        }
                    } else if (mode == ZOOM) {
                        if (event.getPointerCount() > 1) {
                            float newDist = spacing(event);
                            if (newDist > 10f * density) {
                                matrix.set(savedMatrix);
                                float scale = (newDist / oldDist);
                                float[] values = new float[9];
                                matrix.getValues(values);
                                if (scale * values[Matrix.MSCALE_X] >= MAX_ZOOM) {
                                    scale = MAX_ZOOM / values[Matrix.MSCALE_X];
                                }
                                if (scale * values[Matrix.MSCALE_X] <= MIN_ZOOM) {
                                    scale = MIN_ZOOM / values[Matrix.MSCALE_X];
                                }
                                matrix.postScale(scale, scale, mid.x, mid.y);
                                matrix.invert(matrixInverse);
                            }
                        } else {
                            if ( SystemClock.elapsedRealtime() - downTime > 250l) {
                                doubleTap = false;
                            }
                            matrix.set(savedMatrix);
                            float scale = event.getY() / start.y;
                            float[] values = new float[9];
                            matrix.getValues(values);
                            if (scale * values[Matrix.MSCALE_X] >= MAX_ZOOM) {
                                scale = MAX_ZOOM / values[Matrix.MSCALE_X];
                            }
                            if (scale * values[Matrix.MSCALE_X] <= MIN_ZOOM) {
                                scale = MIN_ZOOM / values[Matrix.MSCALE_X];
                            }
                            matrix.postScale(scale, scale, mid.x, mid.y);
                            matrix.invert(matrixInverse);
                        }
                    }
                    break;
            }
    
    
            invalidate();
            return true;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题