The last blog talked about event distribution and event handling of View, and then about ViewGroup. ViewGroup has one more event interception, involving three corresponding methods.
Dispatch TouchEvent Event Event Distribution onInterceptTouchEvent event interception onTouchEvent event handling
First, look at the results of the following different situations.
Normally:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent
ACTION_UP:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent—>View.onClick
Comment out View.onClick
ACTION_DOWN:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent—>ViewGroup.onTouchEvent
Change the onTouchEvent return value in View to true
ACTION_DOWN:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent
ACTION_UP:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>View.dispatchTouchEvent—>View.onTouch—>View.onTouchEvent
Change the onInterceptTouchEvent return value in ViewGroup to true
ACTION_DOWN:
ViewGroup.dispatchTouchEvent—>ViewGroup.onInterceptTouchEvent—>ViewGroup.onTouchEvent
Combining the above results, click on the ViewGroup source code to see and analyze the reasons for the above results.
Click on the source code to find the dispatchTouchEvent() method.
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
...
if (actionMasked == MotionEvent.ACTION_DOWN) {
//Clear out some tags, mainly mFirstTouchTarget
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//intercepted is used to identify event interception by default false
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//Call the event interception method and return the value.
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
...
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
//If intercepted is true, that is to say, event interception, where it returns to true, it will not go this way.ifinside
if (!canceled && !intercepted) {
...
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
...
if (newTouchTarget == null && childrenCount != 0) {
...
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
...
}
if (preorderedList != null) preorderedList.clear();
}
...
}
}
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
...
}
...
return handled;
}
Judge by the action you get. Call cancelAndClearTouchTargets(ev) first for MotionEvent.ACTION_DOWN; clear the target
// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}
/**
* Clears all touch targets.
*/
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
You go to the clearTouchTargets() method and set mFirstTouchTarget to null; after mFirstTouchTarget is set to null, you define an event interception identifier, the pattern is false.
// Check for interception.
final boolean intercepted;
If the event is not intercepted, that is, when intercepted is false, it will call here.
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
//If the child calls dispatchTouchEvent in itself directly for null, otherwise the dispatchTouchEvent in the child is called.
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
return handled;
}
When you get here, you have called ViewGroup. dispatch TouchEvent, ViewGroup. onIntercept TouchEvent, View. dispatch TouchEvent, to View. dispatch TouchEvent, and then you will go to View.onTouch, View.onTouchEvent, View.onClick, which has been mentioned in the event handling of View. At this time, the first result is running.
If the click event of View is blocked, the performClick() method returns false, that is, no consumption event.
Change the return value of onTouchEvent in View to true, as detailed in the event of View.
Change the return value of onInterceptTouchEvent in ViewGroup to true, and the following judgment will not go away.
if (!canceled && !intercepted){
...
}
If you don't go, you won't call the dispatchTransformedTouchEvent method, you won't go to those methods in View, you know by the source code of ViewGroup and View:
If the sub View does not have a place to return true, it will only come in once to respond to the DOWN event, which means that it does not need to consume the event. If it wants to respond to MOVE,UP must find a place to return true.
For ViewGroup, if you want to intercept the touch event of the child View, rewrite onInterceptTouchEvent to return true
If the onInterceptTouchEvent returns true, the onTouchEvent method of ViewGroup will be executed, and if the child view has no consumption event, the onTouchEvent method will also be executed.
If there is something wrong with the event handling of ViewGroup above, welcome to communicate.