Android Touch event Transfer Mechanism
In the previous article, I mainly explained the Touch event transfer process in the Android source code. Now I want to use a demo and an instance to learn about the Touch event handling process in Andorid.
In the Android system, the three functions closely related to Touch event distribution and processing are as follows:
(1) public boolean dispatchTouchEvent (MotionEvent ev)
(2) public boolean onInterceptTouchEvent (MotionEvent ev)
(3) public boolean onTouchEvent (MotionEvent event)
I have analyzed their source code in the previous article: method 1 mainly distributes Touch events, and method 2 mainly intercepts Touch events, method 3: process Touch events
These three methods mainly exist in ViewGroup, View, and Activity, as shown in the following figure:
|
ViewGroup |
View |
Activity |
DispatchTouchEvent |
Yes |
Yes |
Yes |
OnInterceptTouchEvent |
Yes |
None |
None |
OnTouchEvent |
Yes |
Yes |
Yes |
Next we will use a demo to look at the execution process of these methods:
Custom class: MyLayoutFirst. java
public class MyLayoutFirst extends LinearLayout{ private static final String TAG = MyLayoutFirst; public MyLayoutFirst(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.w(yzy, MyLayoutFirst->onInterceptTouchEvent->+MyUtils.getActionName(ev)); return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.e(yzy, MyLayoutFirst->onTouchEvent->+MyUtils.getActionName(event)); return super.onTouchEvent(event); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(yzy, MyLayoutFirst->dispatchTouchEvent->+MyUtils.getActionName(ev)); return super.dispatchTouchEvent(ev); } }
Customize a class; MyLayoutSecond. java
public class MyLayoutSecond extends LinearLayout{ private static final String TAG = MyLayoutSecond; public MyLayoutSecond(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent event) { Log.e(yzy, MyLayoutSecond->MyLayoutSecond->+MyUtils.getActionName(event)); return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.w(yzy, MyLayoutSecond->onInterceptTouchEvent->+MyUtils.getActionName(ev)); return super.onInterceptTouchEvent(ev); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(yzy, MyLayoutSecond->dispatchTouchEvent->+MyUtils.getActionName(ev)); return super.dispatchTouchEvent(ev); } }
Add to main_layout.xml
Add the onTouchEvent method to MainActivity
public class MainActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.i(yzy, MainActivity->dispatchTouchEvent->+MyUtils.getActionName(ev)); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.e(yzy, MainActivity->onTouchEvent->+MyUtils.getActionName(event)); return super.onTouchEvent(event); }}
The last tool class is used to convert the event id to a string.
public class MyUtils{ private static final String TAG = MyUtils; public static String getActionName(MotionEvent event) { String name=; switch(event.getAction()) { case MotionEvent.ACTION_DOWN: name=ACTION_DOWN; break; case MotionEvent.ACTION_MOVE: name=ACTION_MOVE; break; case MotionEvent.ACTION_UP: name=ACTION_UP; break; } return name; }}
Running Effect
The blue part is MyLayoutSecond. java, and the red part is MyLayoutFirst. java.
Now click the blue part: running result
It can be seen that the event is first captured by the Activity and then distributed to MyLayoutFirst. MyLayoutFirst calls its onInterceptTouchEvent to determine whether to intercept the event. Because the default return value is false, the event is not intercepted, in this way, the event is distributed to MyLayoutSecond, and MyLayoutSecond is also distributed through dispatchTouchEvent. Before the event is distributed, check whether it is intercepted. By default, no subview is provided because MyLayoutSecond does not exist, all the final events are handled by themselves and the onTouchEvent method of the method is called. Because this method returns false by default, the event is considered to have not been consumed and is passed to MyLayoutFirst, similarly, this event was not consumed and finally passed to Mainactivity. Later, we can see that ACTION_MOVE and ACTION_UP are not passed into MyLayoutFirst and MyLa. YoutSecond, because once an event is not processed, subsequent events will not be distributed. Therefore, ACTION_MOVE and ACTION_UP are directly processed by MainActivity.
Next, let's look at the second situation:
|
MainActivity |
MyLayoutFirst |
MyLayoutSecond |
DispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
OnInterceptTouchEvent |
-- |
True |
Super. onInterceptTouchEvent (ev) |
OnTouchEvent |
Super. onTouchEvent |
Super. onTouchEvent |
Super. onTouchEvent |
The running result is as follows:
It can be seen that the event is not distributed to MyLayoutSecond after it is passed to MyLayoutFirst, and its onTouchEvent is directly called. Because the returned value is false, the event is not consumed and is finally passed to MainActivity,
In addition, subsequent events are not passed to MyLayoutFirst and MyLayoutSecond and are directly processed by MainActivity.
Case 3:
|
MainActivity |
MyLayoutFirst |
MyLayoutSecond |
DispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
OnInterceptTouchEvent |
-- |
True |
Super. onInterceptTouchEvent (ev) |
OnTouchEvent |
Super. onTouchEvent |
True |
Super. onTouchEvent |
Running result:
Different from scenario 2, MyLayoutFirst's onTouchEvent returns true, that is, MyLayoutFirst consumes this event, so ACTION_DOWN is not passed to MainActivity, and ACTION_MOVE and ACTION_UP
All are passed to MyLayoutFirst
Fourth case:
|
MainActivity |
MyLayoutFirst |
MyLayoutSecond |
DispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
Super. dispatchTouchEvent |
OnInterceptTouchEvent |
-- |
Super. onInterceptTouchEvent (ev) |
Super. onInterceptTouchEvent (ev) |
OnTouchEvent |
Super. onTouchEvent |
Super. onTouchEven |
True |
Running result:
All events are consumed after being passed to MyLayoutSecond.
In fact, there are many other combinations. If you are interested, you can try to change the return values of each function and view the printed results. I will not list them here .....
Finally, I will provide a small demo to demonstrate how to solve the sliding conflict. The background is as follows:
A ViewPager contains two Framgent, and one Fragment contains a HorizontalListView. How to slide and conflict?
I will post the key code.
horizontal=(HorizontalListView)view.findViewById(R.id.hscroll); horizontal.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View arg0, MotionEvent event) { if(event.getAction()==MotionEvent.ACTION_DOWN) { parent.requestDisallowInterceptTouchEvent(true); }else if(event.getAction()==MotionEvent.ACTION_UP) { parent.requestDisallowInterceptTouchEvent(false); } return false; } });
By adding this code, you can avoid slide conflicts. As for why, I will upload and download the two demo examples in my previous article "Android Touch event transfer mechanism details ".