Implementation of multi touch and gesture recognition for Android click events

Time:2021-9-19

preface

Recently, I met the need to realize three finger sliding monitoring. It’s inconvenient to post the implementation code, but the idea can still be recorded.

Muilti touch two finger zoom exploration

First implement the ontouchlistener interface, and then override the method:


public boolean onTouch(View v, MotionEvent event); 

From this method, we can get all the information to realize the two finger scaling function.

View v is the source of triggering events. Motionevent is a touch event. Almost all operations on the screen will trigger events, such as clicking, releasing, sliding, etc.

Different events have different IDS in motionevent. We can use event. Getaction() & motionevent. Action_ The result of mask is used to judge what kind of event it is.

There are the following events that we need to use:

  • MotionEvent.ACTION_ Down: triggered when the first point is pressed
  • MotionEvent.ACTION_ Up: triggered when the only point on the screen is released
  • MotionEvent.ACTION_ POINTER_ Down: triggered when a point on the screen is pressed and other points are pressed.
  • MotionEvent.ACTION_ POINTER_ Up: triggered when multiple points on the screen are pressed and released (that is, when the last point is not released).
  • MotionEvent.ACTION_ Move: triggered when a point moves on the screen. It is worth noting that because of its high sensitivity, and our fingers can’t be completely stationary (even if we can’t feel it moving, our fingers are constantly shaking), the actual situation is that this event will be triggered as long as it is on the screen.

For example, when we put an index finger on the screen, action is triggered_ Down event; Put another middle finger on the screen to trigger action_ POINTER_ Down event; At this time, releasing the index finger or middle finger will trigger action_ POINTER_ Up event; Release the last finger and trigger action_ Up event; At the same time, in the whole process, action_ The move event is constantly triggered.

Event.getx (index) and event.gety (index) can obtain the coordinates of the specified index point, so when there are two points on the screen, we use the following method to obtain the distance between the two points:


private float spacing(MotionEvent event) {  
    float x = event.getX(0) - event.getX(1);  
    float y = event.getY(0) - event.getY(1);  
    return FloatMath.sqrt(x * x + y * y);  
} 

Based on the above event triggering principle, the number of points on the current screen can be determined according to different triggered events:


switch (event.getAction() & MotionEvent.ACTION_MASK) {  
        case MotionEvent.ACTION_DOWN:  
            mode = 1;  
            break;  
        case MotionEvent.ACTION_UP:  
            mode = 0;  
            break;  
        case MotionEvent.ACTION_POINTER_UP:  
            mode -= 1;  
            break;  
        case MotionEvent.ACTION_POINTER_DOWN:  
            mode += 1;  
            break;  
}

Then in motionevent.action_ In the move event, judge the number of points. If it is greater than or equal to 2, calculate the distance between the two points. If the distance increases, enlarge the picture, and if the distance decreases, shrink the picture.

So the code becomes:

switch (event.getAction() & MotionEvent.ACTION_MASK) {  
case MotionEvent.ACTION_DOWN:  
    mode = 1;  
    break;  
case MotionEvent.ACTION_UP:  
    mode = 0;  
    break;  
case MotionEvent.ACTION_POINTER_UP:  
    mode -= 1;  
    break;  
case MotionEvent.ACTION_POINTER_DOWN:  
    oldDist = spacing(event);// Distance when two points are pressed  
    mode += 1;  
    break;        
case MotionEvent.ACTION_MOVE:  
    if (mode >= 2) {  
        float newDist = spacing(event);  
        if (newDist > oldDist) {  
            zoomOut();  
        }  
        if (newDist < oldDist) {  
            zoomIn();  
        }  
        break;  
    }

After testing, this method can achieve the scaling effect.

But there is another problem: because of action_ Move will always be triggered due to shaking, and the distance between two points will always change slightly each time. Therefore, as long as there are two points on the screen after operation, the font will always be enlarged or reduced.

After some thinking, I came up with a method to control its sensitivity, that is, in case motionevent.action_ During move, it is judged that the font size will be changed only when the distance change is greater than a certain degree:

If (newdist > olddist + 1) {// originally: if (newdist > olddist)  
    zoomOut();// enlarge  
}

In addition, the zoom method is also changed to scale by scale. The complete zoomlistenter Code:


import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.TextView;
 
public class ZoomListenter implements OnTouchListener {
 
    private int mode = 0;
    float oldDist;
    float textSize = 0;
 
    TextView textView = null;
 
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        textView = (TextView) v;
        if (textSize == 0) {
            textSize = textView.getTextSize();
        }
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            mode = 1;
            break;
        case MotionEvent.ACTION_UP:
            mode = 0;
            break;
        case MotionEvent.ACTION_POINTER_UP:
            mode -= 1;
            break;
        case MotionEvent.ACTION_POINTER_DOWN:
            oldDist = spacing(event);
            mode += 1;
            break;
 
        case MotionEvent.ACTION_MOVE:
            if (mode >= 2) {
                float newDist = spacing(event);
                if (newDist > oldDist + 1) {
                    zoom(newDist / oldDist);
                    oldDist = newDist;
                }
                if (newDist < oldDist - 1) {
                    zoom(newDist / oldDist);
                    oldDist = newDist;
                }
            }
            break;
        }
        return true;
    }
 
    private void zoom(float f) {
        textView.setTextSize(textSize *= f);
    }
 
    private float spacing(MotionEvent event) {
        float x = event.getX(0) - event.getX(1);
        float y = event.getY(0) - event.getY(1);
        return FloatMath.sqrt(x * x + y * y);
    }
 
}

In this way, the expected effect can be basically achieved.

Gesture monitoring with Android native

Gesturedetector uses

Gesturedetector is an object specially used for gesture monitoring in Android. In his listener, we can monitor various gestures in the callback methods of various events by passing in the motionevents object. For example, the ongesturelistener of gesturedetector is a callback method, that is, after obtaining the passed in motionevents object, it is processed. By rewriting various methods (click event, double-click event, etc.), we can monitor click, double-click, sliding and other events, It is then processed directly within these methods.

usage method

First, create a simpleongesturelistener callback method object and override each of its methods
Based on this listener object, instantiate the gesturedetector object
Rewrite the setOnTouchListener method for the target control and invoke the onTouchEvent method of the detector object.
Easy to understand, one minute.


@Override
    protected void onResume() {
        button.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return detector.onTouchEvent(event);
            }
        });
        super.onResume();
    }
 
    private void iniGestureListener(){
        GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onDoubleTap(MotionEvent e) {
                MyToast.makeToast(GestureDetectorActivity.this, "double  click up!");
                return super.onDoubleTap(e);
            }
 
        detector = new GestureDetector(GestureDetectorActivity.this, listener);
    }

What other powerful callback methods does gesturedetector have?

  • Ondoubletaplistener: that is, double click the event. In addition to the callback method ondoubletapevent, there are two callback methods singletapconfirmed and doubletap
  • Ongesturelistener: there are many gesture listeners: down, fling, longpress, scroll, showpress, and singletapup
  • Simpleongesturelistener: the empty implementation of the above interface is frequently used

OnDoubleTapListener

Let’s talk about ondoubletaplistener first. You may want to ask: didn’t you just talk about double-click event monitoring? Isn’t it a waste of time here? No nonsense, let me introduce such methods in detail:

Click callback singletapconfirmed

Some people will be curious. For the callback of click events, just use onclicklistener directly. Why use singletapconfirmed?

First of all, the two methods are in conflict. Here, the event distribution mechanism is involved. I will summarize the details later when I have time. I won’t explain it in detail here.

Second, more   It is not difficult for us to find the mechanism of onclicklistener. If we use onclicklistener, when we double-click, we will also call the click event, that is, we click twice, which is obviously inconsistent with our intention. So how to call it? As follows:


        final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                MyToast.makeToast(GestureDetectorActivity.this, "single  click!");
                return super.onSingleTapConfirmed(e);
            }
 
            ...
        };

Doubletap and ondoubletapevent

We intend to put these two methods together. On the one hand, they both belong to the category of double-click, and on the other hand, they have very high similarities and subtle but important differences.

You can try to print the down move and up clicked in ondoubletapevent and doubletap, and you will find that for doubletap, it is the callback that occurs when you click and press for the second time, while for ondoubletapevent, it is the callback that occurs when your finger lifts up and leaves the screen after the second click. This is the most important difference between his two.


 final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
 
        @Override
        public boolean onDoubleTap(MotionEvent e) {
            MyToast.makeToast(GestureDetectorActivity.this, "double  click down!");
            return super.onDoubleTap(e);
        }
 
        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            switch (e.getActionMasked()){
                case MotionEvent.ACTION_UP:
                    MyToast.makeToast(GestureDetectorActivity.this, "double  click up!");
                    break;
            }
            return super.onDoubleTapEvent(e);
        }
    };

Therefore, with these two methods, we can more purposefully meet the two needs. Here, the double-click event will come to an end. Let’s enter the learning of ongesturelistener.

OnGestureListener

This can be said to be the core part of the whole gesture monitoring. It was introduced before, and now it is the main topic. Here I mainly introduce gestures to you:

  • Press (down)
  • One throw (fling)
  • Long press
  • Scroll
  • Touch feedback (showpress)
  • Click singletapup

onDown

The ondown event is well understood. It is executed when a view is pressed. That’s right. To execute ondown, first ensure that the view is clickable, that is, the value of onclickable is true.

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
 
        @Override
        public boolean onDown(MotionEvent e) {
            MyToast.makeToast(GestureDetectorActivity.this, "onDown");
            //Subsequent events
            return super.onDown(e);
        }
    };

onFling

For onfling, I personally feel that this is the most commonly used method, just like its name, which translates to drag, drag and throw. For example, we have used recyclerview or listview. When we quickly pull up, we will roll a certain distance to stop. Our lovely onfling is used to detect this gesture.


    private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            mSpeedX = velocityX;
            mSpeedY = velocityY;
            handler.postDelayed(runnable, 30);
            return super.onFling(e1, e2, velocityX, velocityY);
        }
    };

From the code, it is not difficult to find that the method has four parameters

parameter significance
e1 Event when the finger is pressed.
e2 Event when the finger is raised.
velocityX The speed of motion on the x-axis (pixels / second).
velocityY The speed of motion on the y-axis (pixels / second).

Through the first two motionevent parameters, we can obtain the position where the click occurs, etc. through the last two float parameters, we can obtain the speed of finger sliding.

In fact, there are many specific uses. For example, we can imagine a billiards game. After the club hits the ball, there is such an effect of decreasing the initial speed.

onLongPress

Onlongpress is very simple, which is the callback of long press events, such as long press copy, long press pop-up window, etc. it is not only widely used, but also very simple to use. There is no nagging here

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
 
        @Override
        public void onLongPress(MotionEvent e) {
            MyToast.makeToast(GestureDetectorActivity.this, "onLongPress");
            //Follow up work
            super.onLongPress(e);
        }
    };

onScroll

The onscroll method is very similar to onfling. The only difference is that the parameter of onfling is the sliding speed, while the last two parameters of onscroll are the sliding distance:

parameter significance
e1 Motionevent when a finger is pressed
e2 Motionevent with finger raised
distanceX The distance across the X axis
distanceY The distance across the Y axis

    private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            MyToast.makeToast(GestureDetectorActivity.this, "onScroll X = " + 
                    distanceX + " Y = " + distanceY);
            return super.onScroll(e1, e2, distanceX, distanceY);
        }
    };

onShowPress

In fact, I don’t think this method is very effective, because it is called when the view is clicked (pressed). Its function is to give the user a visual feedback and let the user know that my control has been clicked. Such an effect can be realized by using the ripple of material design or directly writing a background.

If there is anything special, it is a delayed callback with a delay time of 180 Ms. In other words, if the user’s finger is pressed and the event is immediately lifted or intercepted, and the time does not exceed 180 ms, the message will be removed, and the callback will not be triggered.

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        public void onShowPress(MotionEvent e) {
            MyToast.makeToast(GestureDetectorActivity.this, "onShowPress");// > Called at 150ms
            super.onShowPress(e);
        }
    };

onSingleTapUp

There are a lot of analysis on onsingletapup, but I think it’s too complicated. In fact, it’s very simple. For example, you will understand:

We talked about the double-click event before. Well, onsingletapup is a callback when the double-click event is clicked for the first time. In other words, when you click a control (double-click the first time), the callback will be called immediately, and then quickly click the second time (double-click the second time of the event), it will not be called.

type Trigger times abstract
onSingleTapUp 1 Triggered on the first lift of a double-click
onSingleTapConfirmed 0 Double clicking does not trigger.
onClick 2 Triggered twice when the event is double clicked.

The difference between onsingletapconfirmed and onsingletapconfirmed is obvious. Onsingletapconfirmed will not call back when double clicking, while onsingletapup will only call back when double clicking for the first time.

private final GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
        @Override
        Public Boolean onsingletapup (motionevent E) {// double click to trigger the first lift, but not the second
            Log.d("onSingleTapUp", "onSingleTapUp");// > Called at 150ms
            return super.onSingleTapUp(e);
        }
    };

SimpleOnGestureListener

Simpleongesturelistener contains the empty implementation of all the above methods. The reason to mention it again at the end of the article is mainly to talk about its convenience.

Let’s take listening to ondoubletaplistener as an example. If you want to use the ondoubletaplistener interface, you need to set it as follows:


GestureDetector detector = new GestureDetector(this, new GestureDetector
        .SimpleOnGestureListener());
detector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() {
    @Override public boolean onSingleTapConfirmed(MotionEvent e) {
        Toast.makeText(MainActivity.this, "onSingleTapConfirmed", Toast.LENGTH_SHORT).show();
        return false;
    }
 
    @Override public boolean onDoubleTap(MotionEvent e) {
        Toast.makeText(MainActivity.this, "onDoubleTap", Toast.LENGTH_SHORT).show();
        return false;
    }
 
    @Override public boolean onDoubleTapEvent(MotionEvent e) {
        Toast.makeText(MainActivity.this,"onDoubleTapEvent",Toast.LENGTH_SHORT).show();
        return false;
    }
});

It is not difficult to find a problem. Since a simpleongesturelistener has been instantiated when gesturedetector is instantiated, there will be several useless empty implementations when using onggesturelistener, which is obviously wasteful. Therefore, in general, it is good to use simpleongesturelistener obediently.

other

Android not only provides a gesturedetector to help us recognize some basic touch gestures, but also scalegestuuredetector can recognize zoom gestures, which makes it easy for us to realize gesture control.


//-----------------------implement OnScaleGestureListener's method----------------------//
 
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        Toast.makeText(MainActivity.this, "onScale", Toast.LENGTH_SHORT).show();
        return true;
    }
 
    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        Toast.makeText(MainActivity.this, "onScaleBegin", Toast.LENGTH_SHORT).show();
        return true;
    }
 
    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        Toast.makeText(MainActivity.this, "onScaleEnd", Toast.LENGTH_SHORT).show();
    }

This is the end of this article on the implementation of multi touch and gesture recognition for Android click events. For more information about Android multi touch and gesture recognition, please search previous articles of developeppaer or continue to browse the relevant articles below. I hope you will support developeppaer in the future!