PS: In this era of widespread knowledge anxiety, everyone should understand the importance of learning and strive to become a "person who knows how to learn" rather than a "diligent fool".
The event distribution process has been basically explained. Before reading this article, please read the following articles:
- Android Event Distribution Basics
- Android Event Distribution Source Code Analysis
- Android Event Distribution Process Analysis
There is also a question about how the onTouch and onClick events are passed during the Android event transmission process. The following mainly introduces the order of calling onTouch, onClick, and the event transmission process from the following aspects:
- onTouch() method in the source code
- onClick() method in the source code
- Relationship between onTouch() and onClick() methods
- Summary
onTouch() method in the source code#
When setting the touch event listener, the OnTouchListener interface in the View class is used, and then the touch event listener is set through setOnClickListener. Then, specific operations can be performed based on the specific event type. The onTouch() method is the method defined in the OnTouchListener interface and is called in the dispatchTouchEvent() method of the View. The following is the specific call to the onTouch method in the source code:
// Event dispatch
public boolean dispatchTouchEvent(MotionEvent event) {
...
// Default return value
boolean result = false;
...
// Pay attention to the judgment condition
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
ListenerInfo li = mListenerInfo;
// Pay attention to the judgment condition
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
In the above code, let's first look at the outermost condition onFilterTouchEventForSecurity() method. We only need to pay attention to the return value of this method. The source code is as follows:
/**
* Returns true if the event should be dispatched, false if the event should be dropped.
* @see #getFilterTouchesWhenObscured
*/
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
//noinspection RedundantIfStatement
if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
// Window is obscured, drop this touch.
return false;
}
return true;
}
So under normal circumstances, this method returns true. The key conditions are whether ListenerInfo is null, whether mOnTouchListener is null, and the return value of onTouch() method. The following is the source code part of ListenerInfo initialization:
// Specific call to getListenerInfo()
public void setOnTouchListener(OnTouchListener l) {
getListenerInfo().mOnTouchListener = l;
}
// Initialization of ListenerInfo
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
}
mListenerInfo = new ListenerInfo();
return mListenerInfo;
}
Obviously, when setting the touch event listener through setOnTouchListener(), ListenerInfo is initialized. At the same time, mOnTouchListener != null is established when setting the touch event listener. The return value of onTouch() method determines whether the dispatchTouchEvent() method returns true. Therefore, when the touch event listener is set and onTouch() method returns true, it means that the event is handled here and will not be passed to the child View. At the same time, the onTouchEvent() method will not be executed if it returns false.
onClick() method in the source code#
When setting the click event listener, the OnClickListener interface in the View class is used, and then the click event listener is set through setOnClickListener. Then, specific operations can be performed based on the specific event type. The onClick() method is the method defined in the OnClickListener interface and is called in the onTouchEvent() method of the View. This will be further verified in the following text. The following is the specific call to the onClick() method in the source code:
// Event handling
public boolean onTouchEvent(MotionEvent event) {
...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
...
// If this method is executed, its return value is the return value of onTouchEvent()
performClick();
...
break;
}
return true;
}
return false;
}
In the above code, at least the call location of the onClick() method is found. The following is the performClick() method:
/**
* Mainly callback OnClickListener
*/
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
// Call the onClick() method in the OnClickListener interface
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
Obviously, as long as we set the click event listener through setOnClickListener() method, the corresponding View's onTouchEvent() method returns true. Of course, the event will be consumed. Otherwise, it returns false, and then onTouch and onClick, how is the order of calls between them, and do they affect each other? The relationship between them will be understood from the perspective of the case.
Relationship between onTouch() and onClick() methods#
It is still the previous case, MLinearLayout nested in MRelativeLayout, MRelativeLayout nested in MTextView. All three Views only override the event distribution related to themselves. Then, set the touch event and click event listeners for MTextView. The specific code is as follows:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.textView).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("Event", "TextView-------onTouch---------------return:" + false);
return false;
}
});
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("Event", "TextView-------onClick");
}
});
}
- Let onTouch() return false, and check the logs as follows:
Conclusion: When the touch event listener is set, if onTouch() returns false, onTouchEvent() method is executed after the onTouch() method, the event is consumed here, and then a series of events after ACTION_DOWN are received. In this case, a mouse is used, so there is no ACTION_MOVE event. In the case where onTouch() method returns false, onClick() is also executed.
- Let onTouch() return true, and check the logs as follows:
Conclusion: When onTouch() returns true, as mentioned earlier, onTouchEvent() will not be executed again. Therefore, onClick() will not be executed either.
Summary#
The return value of the onTouch() method determines whether the onTouchEvent() method should be executed. If onTouch() returns true, onTouchEvent() will not be executed. If it returns false, onTouchEvent() will continue to be executed. The callback of onClick() is called in the onTouchEvent() method. If onTouchEvent() is not executed, onClick() will not be executed either.