text
Multi-touch has always been an obscure topic in event processing. First, because its mechanism is a bit different from our conventional thinking, and second, because we use it less. So as an Android developer who is a bit pursuing, we must master these things so that we can improve the style of the code.
It is still a bit difficult to write this article. I have revised it over and over again and again, but I have really deleted it again and again, just to make the multi-touch clearly. Finally, I decided to divide this article into three parts for explanation
- Explain some key concepts of multi-finger touch. Although this part of the concept is very abstract and cannot be explained by source code (the source code is at the bottom), this part of the concept is the most critical. If you want to master multi-touch, you must understand and remember these concepts.
- Explain how multi-finger touch events are distributed and processed in ViewGroup. Because only by understanding this can we write the correct multi-finger touch event code.
- Use an example to explain how to support multi-finger swiping in a sliding control.
OK, let's start this pleasant journey.
Touch Events
First we()
Let's start. Many places call the return value of this method the type of touch event. In fact, this is wrong. Its return value not only contains the type of event, but also contains the index value of the finger.
if()
Return a value expressed in hexadecimal0X0100
, the value of the higher eight digits of this value is01
, in binary representation0000 0001
, which represents the index of the finger, and the value of the lower eight digits is00
, in binary representation0000 0000
, it represents the type of event.
Event Type
So how do we get the type of this event? I think everyone should have thought of the mask of the event type.()
It is to obtain the event type through the event type mask.
So, why do you keep saying()
What is the event type returned? Because this is a coincidence, for one-finger operation,()
In the return value of , the index value of the higher eight bits is 0, so it is exactly the same as the value of the event type.
For multi-finger operation,()
The event index of the return value is no longer 0, it will change as the finger increases, so()
This is the correct operation to return event type.
So let's take a look at the types of events supported by multi-finger touch
Event Type | Event description |
---|---|
ACTION_DOWN | Press the first finger |
ACTION_POINTER_DOWN | Press other fingers |
ACTION_MOVE | Finger movement |
ACTION_POINTER_UP | Not the last finger to lift |
ACTION_UP | The last finger is raised |
Let’s use an example to explain the triggering timing of these events.
- When the first finger is pressed, the event type that is triggered is
ACTION_DOWN
。 - It will trigger when there is a second, or even more finger pressed
ACTION_POINTER_DOWN
event. - When any finger slides, it will trigger
ACTION_MOVE
event. - When the finger is not the last, it will trigger
ACTION_POINTER_UP
event. - When the last finger is selected, it will trigger
ACTION_UP
event.
Finger index
()
There is also a mysterious finger index in the return value, which can be passed()
Get it. So what's the use of it? There is no use for a single finger, but for multiple fingers, it will have a great effect, which can obtain information about the touch event of the finger, for example(int pointerIndex)
Get the X coordinate value.
Finger ID
I wonder if you have noticed it in the event type section just now.ACTION_MOVE
There is no distinction between fingers, so how do we know which finger triggered itACTION_MOVE
What about? Did you think of finger index the first time? Please give up this idea!
People can observe the order of fingers pressed through their eyes, but hardware and software cannot do it, and the index of fingers may change in events. So a serious question arises, how to track a finger? usePointerId
! As for the principle, I don't know much.
So how to get a fingerPointerId
Woolen cloth? When encounteringACTION_DOWN
andACTION_POINTER_DOWN
When you get it through the following code
// Get the index of the fingerint pointerIndex = (); // Get the finger ID through the finger indexint pointerId = (pointerIndex);
In the previous finger index section, we know that through the index, we may obtain information about events, such as coordinate values, as shown in the following code
// Get the finger index int pointerIndex = (); // Get coordinate value float x = (pointerIndex); float y = (pointerIndex);
However, inACTION_MOVE
In the event, we want to obtain the coordinate value of a certain finger. What should we do? First we need to save it inACTION_DOWN
andACTION_POINTER_DOWN
Save fingersPointerId
value, then pass thisPointerId
Call(int pointerId)
Get the index value of the finger, and finally get the coordinate value through the index value. The code is as follows
case MotionEvent.ACTION_MOVE: // Get the index of a finger based on PointerId int pointerIndex = (mPrimaryPointerId); // Get coordinate value float x = (pointerIndex); float y = (pointerIndex); break;
Multi-finger event handling
For multi-finger touch events, there are actually only more than one-finger touchACTION_POINTER_DOWN
andACTION_POINTER_UP
Two events, then these two events areViewGroup
How is it distributed and processed? If you want to use source code to analyze it, this article is too long, but it happens that these two events areACTION_MOVE
The distribution processing process is the same. If you don't understandACTION_MOVE
How it is distributed and processed, please refer to what I wrote beforeViewGroup event distribution and processing source code analysis。
Supports multi-finger sliding controls
After mastering the basic knowledge above, we are now in the practical stage that we like. In this part, we need to make a sliding control support multi-finger sliding.
Before implementing this function, we must clarify the implementation ideas
- Only the main finger can control the sliding of the control.
- If you press a finger, you think that this finger is the main finger.
- When a finger is raised, if it is the main finger, you must find a new finger as the new main finger.
First we need a slidable control, which is taken fromTeach you how to write event processing code step by stepI need everyone to understand the sliding controls of this article clearly, because I will not explain these basic knowledge in the code written below.
We said before,ACTION_POINTER_DOWN
andACTION_POINTER_UP
The processing flow is andACTION_MOVE
Same, so should we cut it off? It depends on what we need to do when encountering these two events.
According to the second line in the implementation idea, if a finger is pressed, it is considered to be the main finger, so it is being processedACTION_POINTER_DOWN
Just simply getting your fingersPointerId
, then save as the main finger, so there is no need to cut it off.
According to the third implementation idea, if the main finger is raised, then you need to find a replacement finger as the main finger, so there is no need to cut it off.
Then, inonInterceptTouchEvent()
andonTouchEvent()
The processing method is the same. First, let's look at the code that saves the main finger as follows
public boolean onInterceptTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_DOWN: onPrimaryPointerDown(ev); break; case MotionEvent.ACTION_POINTER_DOWN: onPrimaryPointerDown(ev); break; } return (ev); } public boolean onTouchEvent(MotionEvent event) { switch (()) { case MotionEvent.ACTION_POINTER_DOWN: onPrimaryPointerDown(event); break; } return true; } /** * When a new finger presses it, it is considered to be the main finger, so the coordinates of the pressed point are re-recorded and the latest X coordinates are updated. * * @param event Touch event. */ private void onPrimaryPointerDown(MotionEvent event) { // Get the finger index int pointerIndex = (); // Get the finger ID through the finger index mPrimaryPointerId = (pointerIndex); // Save coordinate values through finger index mLastX = mStartX = (pointerIndex); mStartY = (pointerIndex); }
Then, let's take a look at how to find a replacement main finger when the main finger is raised
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_POINTER_UP: onPrimaryPointerUp(ev); break; } return (ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (()) { case MotionEvent.ACTION_POINTER_UP: onPrimaryPointerUp(event); break; } return true; } /** * When the main finger is raised, look for a new main finger and update the latest X coordinate value to the X coordinate value of the new main finger. * * @param event */ private void onPrimaryPointerUp(MotionEvent event) { // Get the index value of the raised finger int pointerIndex = (); // Get the ID of the raised finger by indexing the value int pointerId = (pointerIndex); // If the ID of the raised finger is equal to the ID of the main finger if (pointerId == mPrimaryPointerId) { // Find an existing finger index final int newPointerIndex = pointerIndex == 0 ? 1 : 0; // Get the finger ID through the new finger index mPrimaryPointerId = (newPointerIndex); // Get coordinate values through new finger index mLastX = (newPointerIndex); } }
After solving these problems, when dealing with the sliding code, you must use this main finger ID to obtain the coordinate value, and then decide the sliding based on these coordinate values. I will use some code to demonstrate here.
public boolean onInterceptTouchEvent(MotionEvent ev) { switch (()) { case MotionEvent.ACTION_MOVE: // Get the coordinate value of the main finger PointF primaryPointerPoint = getPrimaryPointerPoint(ev); //Judge whether sliding is required based on the coordinate value if (canScroll(, )) { mBeingDragged = true; getParent().requestDisallowInterceptTouchEvent(true); // Perform a swipe performDrag(); mLastX = ; // Cut the event if it can be swiped return true; } break; } return (ev); } /** * Gets the coordinates of the main finger when an event is triggered. * * @param event Touch event. * @return If successful, return coordinate point, otherwise return null. */ private PointF getPrimaryPointerPoint(MotionEvent event) { PointF pointF = null; if (mPrimaryPointerId != INVALID_POINTER_ID) { int pointerIndex = (mPrimaryPointerId); if (pointerIndex != -1) { pointF = new PointF((pointerIndex), (pointerIndex)); } } return pointF; }
Summarize
To master multiple finger sliding, you must first master its key concepts. With these concepts we can know when the event will trigger and how to track a finger. Then we need to master the process of multi-finger events. Coincidentally, just knowACTION_MOVE
The process of processing understands the process of multi-finger events. Finally, we need to master the implementation idea of adding multi-finger support to a sliding control.
With these three steps, you can basically implement a control that supports multi-finger sliding. But please note that my wording is basically, basically, basically!
Finally, I left one silentlygithubAddress for your reference.
The above is the detailed content of Android development multi-finger touch event processing. For more information about Android multi-finger touch, please follow my other related articles!