Signal detection

Android gesture detection, also known as the use of GestureDetector. The GestureDetector can use MotionEvents to detect various gestures and events. GestureDetector OnGestureListener is a callback methods, in the event of a specific event callback Listener will call the corresponding method. This class can only be used to detect motionEvents for touch events, not trackball events.

Using the step

  1. Create an instance of GestrueDetector
  2. In the onTouchEvent(MontionEvent) method, be sure to call the onTouchEvent(MotionEvent) of the GestrueDetector instance. The methods defined in the callback are executed when the event occurs.
  3. If you listen for onContextClick (MotionEvent), The GestureDetector onGenericMotionEvent (MotionEvent) must be called in the View’s onGenericMotionEvent (MotionEvent).

GestureDetector

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package android.view;

import android.content.Context;
import android.os.Handler;

public class GestureDetector {

	/** * GestureDetector has 5 constructors * but the first two are abandoned, the first one is repeated. * So, there are only two to focus on. * * /

	/** ** **/
    @Deprecated
    public GestureDetector(GestureDetector.OnGestureListener listener, Handler handler) {
        throw new RuntimeException("Stub!");
    }

    /** ** **/
    @Deprecated
    public GestureDetector(GestureDetector.OnGestureListener listener) {
        throw new RuntimeException("Stub!");
    }
	
    /** * pay attention to the constructor * context: context * listener: gesture listener **/
    public GestureDetector(Context context, GestureDetector.OnGestureListener listener) {
        throw new RuntimeException("Stub!");
    }
	
    /** * pay attention to the constructor * context: context * listener: gesture listener * handler: Since the GestureDetector automatically creates a Handler internally for processing data, if the GestureDetector is created on the main thread, Its internal Hanlder automatically gets the main thread's Looper. * However, if the GestureDetector is created in a child thread that did not create a Looper, it needs to pass a Handler with a Looper to it, otherwise the creation will fail because the Looper will not be available. * * /
    public GestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler) {
        throw new RuntimeException("Stub!");
    }

    public GestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused) {
        throw new RuntimeException("Stub!");
    }

    public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) {
        throw new RuntimeException("Stub!");
    }

    public void setContextClickListener(GestureDetector.OnContextClickListener onContextClickListener) {
        throw new RuntimeException("Stub!");
    }

    public void setIsLongpressEnabled(boolean isLongpressEnabled) {
        throw new RuntimeException("Stub!");
    }

    public boolean isLongpressEnabled(a) {
        throw new RuntimeException("Stub!");
    }

    public boolean onTouchEvent(MotionEvent ev) {
        throw new RuntimeException("Stub!");
    }

    public boolean onGenericMotionEvent(MotionEvent ev) {
        throw new RuntimeException("Stub!");
    }

	/** * Four gesture listeners: * OnContextClickListener * OnDoubleTapListener * OnGestureListener * SimpleOnGestureListener **/
	
	/** * Empty implementations of the other three interfaces. * In general, use this one. It's more convenient. * * /
    public static class SimpleOnGestureListener implements GestureDetector.OnGestureListener.GestureDetector.OnDoubleTapListener.GestureDetector.OnContextClickListener {
        public SimpleOnGestureListener(a) {
            throw new RuntimeException("Stub!");
        }

        public boolean onSingleTapUp(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public void onLongPress(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            throw new RuntimeException("Stub!");
        }

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            throw new RuntimeException("Stub!");
        }

        public void onShowPress(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onDown(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onDoubleTap(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onDoubleTapEvent(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onSingleTapConfirmed(MotionEvent e) {
            throw new RuntimeException("Stub!");
        }

        public boolean onContextClick(MotionEvent e) {
            throw new RuntimeException("Stub!"); }}/** ** Added by Android6.0(API23) * is used to detect whether buttons on external devices are pressed, * for example, buttons on bluetooth stylus. In general, ignore it. * * /
    public interface OnContextClickListener {
        boolean onContextClick(MotionEvent var1);
    }
	
	/** * Double click event, there are three callback types: * double click (DoubleTap), SingleTapConfirmed (DoubleTapEvent), and double click event callback (DoubleTapEvent) **/
    public interface OnDoubleTapListener {
        boolean onSingleTapConfirmed(MotionEvent var1);

        boolean onDoubleTap(MotionEvent var1);

        boolean onDoubleTapEvent(MotionEvent var1);
    }
	
	/** * Gestures have the following types of events: * Down, LongPress, Scroll, ShowPress and SingleTapUp **/
    public interface OnGestureListener {
	
		// Ensure that the control has the ability to consume events to accept subsequent events.
        boolean onDown(MotionEvent var1);
		
		// Listen when the user presses
		// Delay callback, delay time 180ms
		// If the user's finger is lifted immediately after the press or the event is intercepted immediately, within 180 ms, the message will be removed
        void onShowPress(MotionEvent var1);
		
		// Listen when the user clicks lift
		// The difference is the number of times the double click is triggered:
		// onSingleTapUp: 1 time (triggered on the first lift of double click)
		// onSingleTapConfirmed: 0 times (double click will not trigger)
		// onClick: 2 times (triggered twice on the double click event)
		//
		// The trigger sequence is as follows:
		// onSingleTapUp
		// onClick
		// onDoubleTap // <- double-click
		// onClick
        boolean onSingleTapUp(MotionEvent var1);
		
		// Listen for scroll events
		// var1: Event when the finger is pressed
		// var2: Event when the finger is raised
		// var3: the distance across the X axis
		// var4: the distance across the Y axis
        boolean onScroll(MotionEvent var1, MotionEvent var2, float var3, float var4);
		
		// Detect the long press event
        void onLongPress(MotionEvent var1);
		
		// Throw, throw, common with the list
		// var1: Event when the finger is pressed
		// var2: Event when the finger is raised
		// var3: speed in pixels per second on the X axis
		// var4: Y speed in pixels per second
        boolean onFling(MotionEvent var1, MotionEvent var2, float var3, float var4); }}Copy the code

The sample

Use a constructor without Handler

    // Create a listener callback
    final GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener(){
        /** * click 300ms after the event to trigger */
        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // If you want to listen for both double-click and click events, OnClickListener is not recommended for click events for two reasons:
            If onTouchListener consumes an event, it may cause OnClick not to fire properly.
            // Using OnClickListener will trigger twice when a double click event occurs
            Toast.makeText(MainActivity.this."Do not click",Toast.LENGTH_SHORT).show();
            return super.onSingleTapConfirmed(e);
        }
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            Toast.makeText(MainActivity.this.Double click on the "666",Toast.LENGTH_SHORT).show();
            Log.e("TAG"."Triggered on the second press.");
            return super.onDoubleTap(e);
        }

        /** * The onDoubleTapEvent function is executed in the following order:  * onDoubleTap * onDoubleTapEvent - down * onDoubleTapEvent - move * onDoubleTapEvent - up */
        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            switch (e.getActionMasked()){
                case MotionEvent.ACTION_DOWN:
                    Log.e("TAG"."ACTION_DOWN");
                    break;
                case MotionEvent.ACTION_MOVE:
                    Log.e("TAG"."ACTION_MOVE");
                    break;
                case MotionEvent.ACTION_UP:
                    Log.e("TAG"."Triggered on the second lift.");
                    break;
                    default:
                        break;
            }
            return super.onDoubleTapEvent(e); }};// Create a detector
    GestureDetector gestureDetector;
	
    private void initData(a) {
        // Use a constructor without Handler
          gestureDetector = new GestureDetector(this,onGestureListener);

        // Set the data source for the listener
        btn_doubleTap.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                returngestureDetector.onTouchEvent(motionEvent); }}); }Copy the code

Use a constructor with a Handler

	/** * Since the GestureDetector automatically creates an internal Handler for processing data, * if the GestureDetector is created on the main thread, its internal Hanlder will automatically get the main thread's Looper. * You can directly use the Handler without Handler constructor * * but if the GestureDetector is created in a child thread that did not create a Looper, * you need to pass it a Handler with a Looper, otherwise the creation will fail because the Looper will not be available. * You need to use the constructor **/ with Handler
 
	// Create a listener callback
    final GestureDetector.SimpleOnGestureListener onGestureListener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            Toast.makeText(MainActivity.this.Double click on the "666",Toast.LENGTH_SHORT).show();
            return super.onDoubleTap(e); }};// Create a detector
    GestureDetector gestureDetector;
    private void initData(a) {

        // Several ways to use constructors with handlers;
        // 1. Create Handler for main thread
        // The point is that the Handler passed must have Looper
        final Handler handler = new Handler();
        // The hanlder created by the main thread is automatically associated with the main thread Looper
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                gestureDetector = new GestureDetector(MainActivity.this,onGestureListener,handler);
            }
        }).start();

        // 2. Create Handler for child thread
        // The point is that the Handler passed must have Looper
        new Thread(new Runnable() {
            @Override
            public void run(a) {
                Handler handler1 = new Handler(Looper.getMainLooper());
                gestureDetector = new GestureDetector(MainActivity.this,onGestureListener,handler1);
            }
        }).start();

        // 3. If Looper is prepared by the child thread, it can be created directly using the Handler - free constructor
        // The point is that the Handler passed must have Looper
        new Thread(new Runnable() {
            @Override public void run(a) {
                Looper.prepare(); < p style = "box-sizing: border-box! Important
                gestureDetector = new GestureDetector(MainActivity.this,onGestureListener);
            }
        }).start();

        // Set the data source for the listener
        btn_doubleTap.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                returngestureDetector.onTouchEvent(motionEvent); }}); }Copy the code

Relevant methods

In addition to the various listeners, there are several related methods

methods instructions
setIsLongpressEnabled Whether long press events can be triggered. True indicates that long press events are allowed. False indicates that long press events are not allowed.
isLongpressEnabled Determines whether long-press events are allowed to be triggered. True indicates that long-press events are allowed. False indicates that long-press events are not allowed.
onTouchEvent This is one of the most important methods that was demonstrated at the beginning.
onGenericMotionEvent This was added after API 23 to serve OnContextClickListener.
setContextClickListener Set the ContextClickListener.
setOnDoubleTapListener Set the OnDoubleTapListener.

Zooming gesture detection

Generally speaking, zooming gestures do not exist in isolation. If used with custom controls, it is better to use with Matrix related content. Similar to the GestureDetector, it also uses the Listener to listen for the user’s gesture. It encapsulates the zoom gesture.

ScaleGestureDetector

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package android.view;

import android.content.Context;
import android.os.Handler;

public class ScaleGestureDetector {

/** * has two constructors **/
    public ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener) {
        throw new RuntimeException("Stub!");
    }

    public ScaleGestureDetector(Context context, ScaleGestureDetector.OnScaleGestureListener listener, Handler handler) {
        throw new RuntimeException("Stub!");
    }

    public boolean onTouchEvent(MotionEvent event) {
        throw new RuntimeException("Stub!");
    }

    public void setQuickScaleEnabled(boolean scales) {
        throw new RuntimeException("Stub!");
    }

    public boolean isQuickScaleEnabled(a) {
        throw new RuntimeException("Stub!");
    }

    public void setStylusScaleEnabled(boolean scales) {
        throw new RuntimeException("Stub!");
    }

    public boolean isStylusScaleEnabled(a) {
        throw new RuntimeException("Stub!");
    }

    public boolean isInProgress(a) {
        throw new RuntimeException("Stub!");
    }

    public float getFocusX(a) {
        throw new RuntimeException("Stub!");
    }

    public float getFocusY(a) {
        throw new RuntimeException("Stub!");
    }

    public float getCurrentSpan(a) {
        throw new RuntimeException("Stub!");
    }

    public float getCurrentSpanX(a) {
        throw new RuntimeException("Stub!");
    }

    public float getCurrentSpanY(a) {
        throw new RuntimeException("Stub!");
    }

    public float getPreviousSpan(a) {
        throw new RuntimeException("Stub!");
    }

    public float getPreviousSpanX(a) {
        throw new RuntimeException("Stub!");
    }

    public float getPreviousSpanY(a) {
        throw new RuntimeException("Stub!");
    }

    public float getScaleFactor(a) {
        throw new RuntimeException("Stub!");
    }

    public long getTimeDelta(a) {
        throw new RuntimeException("Stub!");
    }

    public long getEventTime(a) {
        throw new RuntimeException("Stub!");
    }
	
	Two gestures listener / * * * * SimpleOnScaleGestureListener: scaling up empty implementations of detector * OnScaleGestureListener: scaling up detector * * /
	
    public static class SimpleOnScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
        public SimpleOnScaleGestureListener(a) {
            throw new RuntimeException("Stub!");
        }

        public boolean onScale(ScaleGestureDetector detector) {
            throw new RuntimeException("Stub!");
        }

        public boolean onScaleBegin(ScaleGestureDetector detector) {
            throw new RuntimeException("Stub!");
        }

        public void onScaleEnd(ScaleGestureDetector detector) {
            throw new RuntimeException("Stub!"); }}public interface OnScaleGestureListener {
		
		// Scaling is triggered (zero or more times), returns true to indicate that the current scaling event has been processed, and the detector accumulates scaling factors, returns false to accumulate scaling factors.
        boolean onScale(ScaleGestureDetector var1);
		
		// The pinch gesture starts, and this method is called (only once) when two fingers are on the screen. If false is returned, the current pinch gesture is not used.
        boolean onScaleBegin(ScaleGestureDetector var1);
		
		// End of zoom gesture
        void onScaleEnd(ScaleGestureDetector var1); }}Copy the code

The sample

public class ScaleGestureDemoView extends View{

    private static final String TAG="ScaleGestureDemoView";

    private ScaleGestureDetector scaleGestureDetector;

    public ScaleGestureDemoView(Context context) {
        super(context);
        init();
    }

    public ScaleGestureDemoView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init(a) {
        scaleGestureDetector=new ScaleGestureDetector(getContext(),new ScaleGestureDetector.SimpleOnScaleGestureListener(){
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                // Pinch gesture triggered
                // Focus on these two values, the scale center point and the scale factor.
                // Center point: add up all the coordinates and divide by the number.
                // Scaling factor: calculate the average distance between each finger and the focal point. After the user's finger moves, divide the new average distance by the old average distance and calculate the scaling ratio.
                Log.e(TAG, "focusX = " + detector.getFocusX());       // Zoom center, x coordinate
                Log.e(TAG, "focusY = " + detector.getFocusY());       // Scale the center y coordinate
                Log.e(TAG, "scale = " + detector.getScaleFactor());   // Scale factor
                return true;
            }

            @Override
            public boolean onScaleBegin(ScaleGestureDetector detector) {
                // The pinch gesture starts
                Log.e(TAG,"Zoom start");
                return true;
            }

            @Override
            public void onScaleEnd(ScaleGestureDetector detector) {
// super.onScaleEnd(detector);
                // End of zoom gesture
                Log.e(TAG,"End of pinch gesture"); }}); }@Override
    public boolean onTouchEvent(MotionEvent event) {
        scaleGestureDetector.onTouchEvent(event);
        return true; }}Copy the code

Composite sample

public class GestureDemoView extends View {

    GestureDetector mGestureDetector;
    ScaleGestureDetector mScaleGestureDetector;

    // Matrix of the current canvas, used to get some state information of the current canvas, such as zoom size, translation distance, etc
    private Matrix mCanvasMatrix = new Matrix();

    // Convert the coordinates of the user's touch to the Matrix needed for the coordinates on the canvas to find the correct zoom center position
    private Matrix mInvertMatrix = new Matrix();

    // All user-triggered zooming, panning and other operations are directly applied to the canvas through the Matrix below,
    // Some initial zooming and panning information calculated by the system is isolated from user operation information to make the operation more intuitive
    private Matrix mUserMatrix = new Matrix();

    private Bitmap mBitmap;

    // Basic zooming and panning information that is independent of the user's gestures
    private float mBaseScale;
    private float mBaseTranslateX;
    private float mBaseTranslateY;

    private Paint mPaint;

    public GestureDemoView(Context context) {
        super(context);
    }

    public GestureDemoView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
        initGesture(context);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        if (mBitmap.getWidth() * 1.0 f / mBitmap.getHeight() > w * 1.0 f / h) {
            mBaseScale = w * 1.0 f / mBitmap.getWidth();
            mBaseTranslateX = 0;
            mBaseTranslateY = (h - mBitmap.getHeight() * mBaseScale) / 2;
        } else {
            mBaseScale = h * 1.0 f / mBitmap.getHeight() * 1.0 f;
            mBaseTranslateX = (w - mBitmap.getWidth() * mBaseScale) / 2;
            mBaseTranslateY = 0; }}@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(6);
        canvas.translate(mBaseTranslateX, mBaseTranslateY);
        canvas.scale(mBaseScale, mBaseScale);

        canvas.save();
        canvas.concat(mUserMatrix);

        mCanvasMatrix = canvas.getMatrix();
        mCanvasMatrix.invert(mInvertMatrix);

        canvas.drawBitmap(mBitmap, 0.0, mPaint);
        canvas.restore();
    }


    / / - signal processing -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

    private void initGesture(Context context) {
        mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                float scale = getMatrixValue(MSCALE_X, mCanvasMatrix);
                mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale);
                //fixTranslate(); // Do not make corrections when the user scrolls, to ensure that the user also responds when the user scrolls, after the user lifts the finger to make corrections
                invalidate();
                return true;
            }

            @Override
            public boolean onDoubleTap(MotionEvent e) {
                if(! mUserMatrix.isIdentity()) { mUserMatrix.reset(); }else {
                    float[] points = mapPoint(e.getX(), e.getY(), mInvertMatrix);
                    mUserMatrix.postScale(MAX_SCALE, MAX_SCALE, points[0], points[1]);
                }
                fixTranslate();
                invalidate();
                return true; }}); mScaleGestureDetector =new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
            @Override
            public boolean onScale(ScaleGestureDetector detector) {
                float scaleFactor = detector.getScaleFactor();
                float fx = detector.getFocusX();
                float fy = detector.getFocusY();
                float[] points = mapPoint(fx, fy, mInvertMatrix);
                scaleFactor = getRealScaleFactor(scaleFactor);
                mUserMatrix.preScale(scaleFactor, scaleFactor, points[0], points[1]);
                fixTranslate();
                invalidate();
                return true; }}); }// Correct scaling
    private void fixTranslate(a) {
        // Perform the predicted calculation on the Matrix and make corrections according to the calculated results
        Matrix viewMatrix = getMatrix();    // Get the Matrix of the current control
        viewMatrix.preTranslate(mBaseTranslateX, mBaseTranslateY);
        viewMatrix.preScale(mBaseScale, mBaseScale);
        viewMatrix.preConcat(mUserMatrix);
        Matrix invert = new Matrix();
        viewMatrix.invert(invert);
        Rect rect = new Rect();
        getGlobalVisibleRect(rect);

        float userScale = getMatrixValue(MSCALE_X, mUserMatrix);
        float scale = getMatrixValue(MSCALE_X, viewMatrix);

        float[] center = mapPoint(mBitmap.getWidth() / 2.0 f, mBitmap.getHeight() / 2.0 f, viewMatrix);
        float distanceX = center[0] - getWidth() / 2.0 f;
        float distanceY = center[1] - getHeight() / 2.0 f;
        float[] wh = mapVectors(mBitmap.getWidth(), mBitmap.getHeight(), viewMatrix);

        if (userScale <= 1.0 f) {
            mUserMatrix.preTranslate(-distanceX / scale, -distanceY / scale);
        } else {
            float[] lefttop = mapPoint(0.0, viewMatrix);
            float[] rightbottom = mapPoint(mBitmap.getWidth(), mBitmap.getHeight(), viewMatrix);

            // If the width is less than the total width, it is horizontally centered
            if (wh[0] < getWidth()) {
                mUserMatrix.preTranslate(distanceX / scale, 0);
            } else {
                if (lefttop[0] > 0) {
                    mUserMatrix.preTranslate(-lefttop[0] / scale, 0);
                } else if (rightbottom[0] < getWidth()) {
                    mUserMatrix.preTranslate((getWidth() - rightbottom[0]) / scale, 0); }}// If the height is less than the total height, the vertical center
            if (wh[1] < getHeight()) {
                mUserMatrix.preTranslate(0, -distanceY / scale);
            } else {
                if (lefttop[1] > 0) {
                    mUserMatrix.preTranslate(0, -lefttop[1] / scale);
                } else if (rightbottom[1] < getHeight()) {
                    mUserMatrix.preTranslate(0, (getHeight() - rightbottom[1]) / scale);
                }
            }
        }
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mGestureDetector.onTouchEvent(event);
        mScaleGestureDetector.onTouchEvent(event);
        if (event.getActionMasked() == MotionEvent.ACTION_UP) {
            fixTranslate();
        }
        return true;
    }


    //--- Tools ------------------------------------------------------------------------------------

    //-- convert coordinates to canvas coordinates --
    private float[] mapPoint(float x, float y, Matrix matrix) {
        float[] temp = new float[2];
        temp[0] = x;
        temp[1] = y;
        matrix.mapPoints(temp);
        return temp;
    }

    private float[] mapVectors(float x, float y, Matrix matrix) {
        float[] temp = new float[2];
        temp[0] = x;
        temp[1] = y;
        matrix.mapVectors(temp);
        return temp;
    }


    // Get the attributes in Matrix --
    private float[] matrixValues = new float[9];
    private static final int MSCALE_X = 0, MSKEW_X = 1, MTRANS_X = 2;
    private static final int MSKEW_Y = 3, MSCALE_Y = 4, MTRANS_Y = 5;
    private static final int MPERSP_0 = 6, MPERSP_1 = 7, MPERSP_2 = 8;

    @IntDef({MSCALE_X, MSKEW_X, MTRANS_X, MSKEW_Y, MSCALE_Y, MTRANS_Y, MPERSP_0, MPERSP_1, MPERSP_2})
    @Retention(RetentionPolicy.SOURCE)
    private @interface MatrixName {}

    private float getMatrixValue(@MatrixName int name, Matrix matrix) {
        matrix.getValues(matrixValues);
        return matrixValues[name];
    }

    //-- limit the zoom scale --
    private static final float MAX_SCALE = 4.0 f;    // Maximum scaling
    private static final float MIN_SCALE = 0.5 f;    // Minimum zoom

    private float getRealScaleFactor(float currentScaleFactor) {
        float realScale = 1.0 f;
        float userScale = getMatrixValue(MSCALE_X, mUserMatrix);    // The user's current zoom
        float theoryScale = userScale * currentScaleFactor;           // Scale the value theoretically

        // If the user is scaling and the theoretical scaling data is greater than 4.0
        if (currentScaleFactor > 1.0 f && theoryScale > MAX_SCALE) {
            realScale = MAX_SCALE / userScale;
        } else if (currentScaleFactor < 1.0 f && theoryScale < MIN_SCALE) {
            realScale = MIN_SCALE / userScale;
        } else {
            realScale = currentScaleFactor;
        }
        returnrealScale; }}Copy the code

note

References:

Signal detection

Zooming gesture detection

Portal:GitHub

Welcome to follow wechat official account:No reason also