Can we use scale gesture detector for pinch zoom in Android?

前端 未结 6 1630
梦毁少年i
梦毁少年i 2020-11-29 02:11

Can we use scale gesture detector for pinch zoom in Android?

相关标签:
6条回答
  • 2020-11-29 02:23

    You can create a reusable class that implements OnTouchListener to accomplish this.

    public class MyScaleGestures implements OnTouchListener, OnScaleGestureListener {       
        private View view;
        private ScaleGestureDetector gestureScale;
        private float scaleFactor = 1;  
        private boolean inScale = false;
    
        public MyScaleGestures (Context c){ gestureScale = new ScaleGestureDetector(c, this); }
    
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            this.view = view; 
            gestureScale.onTouchEvent(event);
            return true;
        }   
    
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = (scaleFactor < 1 ? 1 : scaleFactor); // prevent our view from becoming too small //
            scaleFactor = ((float)((int)(scaleFactor * 100))) / 100; // Change precision to help with jitter when user just rests their fingers //
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            return true;
        }
    
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            inScale = true;
            return true;
        }
    
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) { inScale = false; }
    }
    

    Then assign it as your View's OnTouchListener like so.

    myView.setOnTouchListener(new MyScaleGestures(context));
    

    If you want to add a scrolling ability to the View you will need to implement onScroll from the OnGestureListener interface. You can add this override to the MyScaleGestures class to accomplish this.

    @Override
    public boolean onScroll(MotionEvent event1, MotionEvent event2, float x, float y) {
        float newX = view.getX();
        float newY = view.getY();
        if(!inScale){
            newX -= x;
            newY -= y;
        }
        WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
        Display d = wm.getDefaultDisplay();
        Point p = new Point();
        d.getSize(p);
    
        if (newX > (view.getWidth() * scaleFactor - p.x) / 2){
            newX = (view.getWidth() * scaleFactor - p.x) / 2;
        } else if (newX < -((view.getWidth() * scaleFactor - p.x) / 2)){
            newX = -((view.getWidth() * scaleFactor - p.x) / 2);
        }
    
        if (newY > (view.getHeight() * scaleFactor - p.y) / 2){
            newY = (view.getHeight() * scaleFactor - p.y) / 2;
        } else if (newY < -((view.getHeight() * scaleFactor - p.y) / 2)){
            newY = -((view.getHeight() * scaleFactor - p.y) / 2);
        }
    
        view.setX(newX);
        view.setY(newY);
    
        return true;
    }
    

    The end result of doing all of the above will give you class like this one:

    public class StandardGestures implements OnTouchListener, OnGestureListener, OnDoubleTapListener, OnScaleGestureListener {
        private View view;
        private GestureDetector gesture;
        private ScaleGestureDetector gestureScale;
        private float scaleFactor = 1;
        private boolean inScale;
    
        public StandardGestures(Context c){
            gesture = new GestureDetector(c, this);
            gestureScale = new ScaleGestureDetector(c, this);
        }
    
        @Override
        public boolean onTouch(View view, MotionEvent event) {
            this.view = view;
            gesture.onTouchEvent(event);
            gestureScale.onTouchEvent(event);
            return true;
        }
    
        @Override
        public boolean onDown(MotionEvent event) {
            return true;
        }
    
        @Override
        public boolean onFling(MotionEvent event1, MotionEvent event2, float x, float y) {
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent event) {
        }
    
        @Override
        public boolean onScroll(MotionEvent event1, MotionEvent event2, float x, float y) {
            float newX = view.getX();
            float newY = view.getY();
            if(!inScale){
                newX -= x;
                newY -= y;
            }
            WindowManager wm = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
            Display d = wm.getDefaultDisplay();
            Point p = new Point();
            d.getSize(p);
    
            if (newX > (view.getWidth() * scaleFactor - p.x) / 2){
                newX = (view.getWidth() * scaleFactor - p.x) / 2;
            } else if (newX < -((view.getWidth() * scaleFactor - p.x) / 2)){
                newX = -((view.getWidth() * scaleFactor - p.x) / 2);
            }
    
            if (newY > (view.getHeight() * scaleFactor - p.y) / 2){
                newY = (view.getHeight() * scaleFactor - p.y) / 2;
            } else if (newY < -((view.getHeight() * scaleFactor - p.y) / 2)){
                newY = -((view.getHeight() * scaleFactor - p.y) / 2);
            }
    
            view.setX(newX);
            view.setY(newY);
    
            return true;
        }
    
        @Override
        public void onShowPress(MotionEvent event) {
        }
    
        @Override
        public boolean onSingleTapUp(MotionEvent event) {
            return true;
        }
    
        @Override
        public boolean onDoubleTap(MotionEvent event) {
            view.setVisibility(View.GONE);
            return true;
        }
    
        @Override
        public boolean onDoubleTapEvent(MotionEvent event) {
            return true;
        }
    
        @Override
        public boolean onSingleTapConfirmed(MotionEvent event) {
            return true;
        }
    
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
    
            scaleFactor *= detector.getScaleFactor();
            scaleFactor = scaleFactor < 1 ? 1 : scaleFactor; // prevent our image from becoming too small
            scaleFactor = (float) (int) (scaleFactor * 100) / 100; // Change precision to help with jitter when user just rests their fingers //
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            onScroll(null, null, 0, 0); // call scroll to make sure our bounds are still ok //
            return true;
        }
    
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            inScale = true;
            return true;
        }
    
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            inScale = false;
            onScroll(null, null, 0, 0); // call scroll to make sure our bounds are still ok //
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:23

    TouchImageView

    public class TouchImageView extends ImageView {
        Matrix matrix;
        // 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;
    
        // Remember some things for zooming
        PointF last = new PointF();
        PointF start = new PointF();
        float minScale = 1f;
        float maxScale = 3f;
        float[] m;
        int viewWidth, viewHeight;
    
        static final int CLICK = 3;
    
        float saveScale = 1f;
    
        protected float origWidth, origHeight;
    
        int oldMeasuredWidth, oldMeasuredHeight;
    
        ScaleGestureDetector mScaleDetector;
    
        Context context;
    
        public TouchImageView(Context context) {
            super(context);
            sharedConstructing(context);
        }
    
        public TouchImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            sharedConstructing(context);
        }
    
        private void sharedConstructing(Context context) {
    
            super.setClickable(true);
    
            this.context = context;
    
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
    
            matrix = new Matrix();
    
            m = new float[9];
    
            setImageMatrix(matrix);
    
            setScaleType(ScaleType.MATRIX);
    
            setOnTouchListener(new OnTouchListener() {
    
                @Override
                public boolean onTouch(View v, MotionEvent event) {
    
                    mScaleDetector.onTouchEvent(event);
    
                    PointF curr = new PointF(event.getX(), event.getY());
    
                    switch (event.getAction()) {
    
                        case MotionEvent.ACTION_DOWN:
    
                            last.set(curr);
    
                            start.set(last);
    
                            mode = DRAG;
    
                            break;
    
                        case MotionEvent.ACTION_MOVE:
    
                            if (mode == DRAG) {
    
                                float deltaX = curr.x - last.x;
    
                                float deltaY = curr.y - last.y;
    
                                float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
    
                                float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
    
                                matrix.postTranslate(fixTransX, fixTransY);
    
                                fixTrans();
    
                                last.set(curr.x, curr.y);
    
                            }
    
                            break;
    
                        case MotionEvent.ACTION_UP:
    
                            mode = NONE;
    
                            int xDiff = (int) Math.abs(curr.x - start.x);
    
                            int yDiff = (int) Math.abs(curr.y - start.y);
    
                            if (xDiff < CLICK && yDiff < CLICK)
    
                                performClick();
    
                            break;
    
                        case MotionEvent.ACTION_POINTER_UP:
    
                            mode = NONE;
    
                            break;
    
                    }
    
                    setImageMatrix(matrix);
    
                    invalidate();
    
                    return true; // indicate event was handled
                }
            });
        }
    
        public void setMaxZoom(float x) {
    
            maxScale = x;
        }
    
        private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    
            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
    
                mode = ZOOM;
    
                return true;
            }
    
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
    
                float mScaleFactor = detector.getScaleFactor();
    
                float origScale = saveScale;
    
                saveScale *= mScaleFactor;
    
                if (saveScale > maxScale) {
    
                    saveScale = maxScale;
    
                    mScaleFactor = maxScale / origScale;
    
                } else if (saveScale < minScale) {
    
                    saveScale = minScale;
    
                    mScaleFactor = minScale / origScale;
    
                }
    
                if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
    
                    matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
    
                else
    
                    matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
    
                fixTrans();
    
                return true;
            }
        }
    
        void fixTrans() {
    
            matrix.getValues(m);
    
            float transX = m[Matrix.MTRANS_X];
    
            float transY = m[Matrix.MTRANS_Y];
    
            float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
    
            float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
    
            if (fixTransX != 0 || fixTransY != 0)
    
                matrix.postTranslate(fixTransX, fixTransY);
        }
    
        float getFixTrans(float trans, float viewSize, float contentSize) {
    
            float minTrans, maxTrans;
    
            if (contentSize <= viewSize) {
    
                minTrans = 0;
    
                maxTrans = viewSize - contentSize;
    
            } else {
    
                minTrans = viewSize - contentSize;
    
                maxTrans = 0;
    
            }
    
            if (trans < minTrans)
    
                return -trans + minTrans;
    
            if (trans > maxTrans)
    
                return -trans + maxTrans;
    
            return 0;
        }
    
        float getFixDragTrans(float delta, float viewSize, float contentSize) {
    
            if (contentSize <= viewSize) {
    
                return 0;
    
            }
    
            return delta;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            viewWidth = MeasureSpec.getSize(widthMeasureSpec);
    
            viewHeight = MeasureSpec.getSize(heightMeasureSpec);
    
            //
            // Rescales image on rotation
            //
            if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
    
                    || viewWidth == 0 || viewHeight == 0)
    
                return;
    
            oldMeasuredHeight = viewHeight;
    
            oldMeasuredWidth = viewWidth;
    
            if (saveScale == 1) {
    
                //Fit to screen.
    
                float scale;
    
                Drawable drawable = getDrawable();
    
                if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
    
                    return;
    
                int bmWidth = drawable.getIntrinsicWidth();
    
                int bmHeight = drawable.getIntrinsicHeight();
    
                Log.e("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
    
                float scaleX = (float) viewWidth / (float) bmWidth;
    
                float scaleY = (float) viewHeight / (float) bmHeight;
    
                scale = Math.min(scaleX, scaleY);
    
                matrix.setScale(scale, scale);
    
                // Center the image
    
                float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
    
                float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
    
                redundantYSpace /= (float) 2;
    
                redundantXSpace /= (float) 2;
    
                matrix.postTranslate(redundantXSpace, redundantYSpace);
    
                origWidth = viewWidth - 2 * redundantXSpace;
    
                origHeight = viewHeight - 2 * redundantYSpace;
    
                setImageMatrix(matrix);
    
            }
    
            fixTrans();
        }
    }
    

    MainActivity.java

    public class SecondActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
    
    
            TouchImageView img = new TouchImageView(this);
            img.setImageResource(R.drawable.ic_launcher);
            img.setMaxZoom(4f);
            setContentView(img);
        }
    }
    
    0 讨论(0)
  • 2020-11-29 02:29

    yes we can here is the sample code where onPinch() and onZoom() are actions to be implement on your own

    public class simpleOnScaleGestureListener extends
            SimpleOnScaleGestureListener {
    
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            startScale = detector.getScaleFactor();
            return true;
        }
    
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }
    
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
            endScale = detector.getScaleFactor();
    
    
    
            if (startScale > endScale) {
                Log.i("onScaleEnd", "Pinch Dection");
                onPinch();
            } else if (startScale < endScale) {
                Log.i("onScaleEnd", "Zoom Dection");
                onZoom();
            }
    
    
        }
    
    }
    
    0 讨论(0)
  • 2020-11-29 02:30

    You can use this

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.drawable.BitmapDrawable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.ScaleGestureDetector;
    import android.view.View;
    
    public class MyImageView extends View {
    
    private static final int INVALID_POINTER_ID = -1;
    
        private Drawable mImage;
        private float mPosX;
        private float mPosY;
    
        private float mLastTouchX;
        private float mLastTouchY;
        private int mActivePointerId = INVALID_POINTER_ID;
    
        private ScaleGestureDetector mScaleDetector;
        private float mScaleFactor = 1.f;
    
        public MyImageView(Context context) {
            this(context, null, 0);
        mImage=act.getResources().getDrawable(context.getResources().getIdentifier("imag­ename", "drawable", "packagename"));
    
            mImage.setBounds(0, 0, mImage.getIntrinsicWidth(), mImage.getIntrinsicHeight());
        }
    
        public MyImageView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public MyImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            // Let the ScaleGestureDetector inspect all events.
            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;
                mActivePointerId = ev.getPointerId(0);
                break;
            }
    
            case MotionEvent.ACTION_MOVE: {
                final int pointerIndex = ev.findPointerIndex(mActivePointerId);
                final float x = ev.getX(pointerIndex);
                final float y = ev.getY(pointerIndex);
    
                // Only move if the ScaleGestureDetector isn't processing a gesture.
                if (!mScaleDetector.isInProgress()) {
                    final float dx = x - mLastTouchX;
                    final float dy = y - mLastTouchY;
    
                    mPosX += dx;
                    mPosY += dy;
    
                    invalidate();
                }
    
                mLastTouchX = x;
                mLastTouchY = y;
    
                break;
            }
    
            case MotionEvent.ACTION_UP: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
    
            case MotionEvent.ACTION_CANCEL: {
                mActivePointerId = INVALID_POINTER_ID;
                break;
            }
    
            case MotionEvent.ACTION_POINTER_UP: {
                final int pointerIndex = (ev.getAction() & 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;
        }
    
        @Override
        public void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.save();
            Log.d("DEBUG", "X: "+mPosX+" Y: "+mPosY);
            canvas.translate(mPosX, mPosY);
            canvas.scale(mScaleFactor, mScaleFactor);
            mImage.draw(canvas);
            canvas.restore();
        }
    
        private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                mScaleFactor *= detector.getScaleFactor();
    
                // Don't let the object get too small or too large.
                mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
    
                invalidate();
                return true;
            }
        }
    
    }
    

    to call this in your activity.setContentView(new MyImageView(this));

    0 讨论(0)
  • 2020-11-29 02:30

    actually there is a library that uses this class just for the zooming of images.

    it's called "TouchImageView"

    0 讨论(0)
  • 2020-11-29 02:31

    ScaleGestureDetector is available starting in Android 2.2 (aka Froyo, API level 8). See: http://android-developers.blogspot.com/2010/06/making-sense-of-multitouch.html

    In 2.0/2.1, you don't have ScaleGestureDetector, but you can provide pinch-to-zoom using the ZDNet blog entry by Ed Burnette that Pieter888 linked to above: http://www.zdnet.com/blog/burnette/how-to-use-multi-touch-in-android-2-part-6-implementing-the-pinch-zoom-gesture/1847

    0 讨论(0)
提交回复
热议问题