break the ice
In the last article, we analyzed a wave of events in detail from Activity -> PhoneWindow -> DecorView -> ViewGroup.
In this article, we mainly use source code to analyze the process of an event passing to View's dispatchTouchEvent (ev) method through ViewGroup's dispatchTouchEvent (ev) method. (Let's first assume that the ViewGroup is full of Views and there is no ViewGroup. Because if there is a ViewGroup in a ViewGroup, the process after him is the same as that passed to him by his father's ViewGroup.
Next, the rules of event delivery
Now the ACTION_DOWN event is passed to View through the following code. This is passed to the View.dispatchTouchEvent(ev) method.
//In this way, the ViewGroup event is passed to child's dispatch TouchEvent (ev)
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispa tchTouchEvent(event);
} else {
//Because it's traversed through ViewGroup, the child must not be null, so it executes this method and passes the event to the child.
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
...
}
Okay, next, let's not say much about the source code of View. dispatch TouchEvent (ev), but we'll just look at the key code.
android.view.View
//It looks very simple. There is very little code.
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
//So let's first acquiesce that it's true and go straight in to see the code inside.
if (onFilterTouchEventForSecurity(event)) {
ListenerInfo li = mListenerInfo;
//This if statement should look very clear.
//The main judgments are: 1. Whether OnTouchListener is set in the current View, 2. And whether the onTouch() method returns true or not.
//If both of the above conditions are true, then result=true;
//If one of the above two conditions is not valid, then result=false;
//Simply look down.
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
//If both of the above conditions are true, then result=true, the if will not go, that is to say, the onTouchEvent (ev) of View will not be executed.
//This conclusion was mentioned in the last article, and here we give an explanation.
//result=true, then it's the final return result; that's dispatchTouchEvent () of ViewGroup.
//The method also returns true. At this point ACTION_DOWN is over, and the event is handled by the ouTouch method and consumed by the current View.
//If the above condition is not valid, either OnTouchListener is not set or onTouch () is set, but onTouch () returns false.
//Then result=false, then onTouchEvent (event) method will be used.
//Then look at the return value of onTouchEvent.
//If true is returned, then result is also true. Represents that the ACTION_DOWN event is handled by the onTouchEvent (event) method of View.
//If false is returned, the result is also false. This is the dispatchTouchEvent() method that tells ViewGroup.
//My View doesn't handle events. Please be smart and see if other Views are handled.
//Note that handling or not handling here is just a sign, and it doesn't mean that you actually handled the event. For example, I on Touch Event (event)
//I just returned false, but I did something else according to ACTION_DOWN. Or I just go back to true, but mine
//The onTouchEvent (event) contains empty code, nothing, and returns true directly.
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
return result;
}
Through the above analysis, we verify that the priority of OnTouchListener is higher than that of onTouchEvent (ev) method. As you can see, without OnTouchListener, all judgments are concentrated in the onTouchEvent (ev) method.
So go directly to the onTouchEvent (ev) source code.
public boolean onTouchEvent(MotionEvent event) {
//First, determine whether the View can click CLICKABLE or whether it can click LONG_CLICKABLE for a long time.
//If there is a clickable set up, it is true. Otherwise, it is false.
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//Next, determine whether the View is in the DISAB LED state.
if ((viewFlags & ENABLED_MASK) == DISABLED) {
if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
//Here we can see. Even if View is currently in the DISAB LED state, as long as View is clickable and long Click
//Here we all return true. That is to say, the DISABLED state consumes events, but nothing is done to deal with them.
return clickable;
}
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_DOWN:
//First of all, under the understanding of a brief answer.
//When ACTION_DWON comes in, it tells the View that the place has been pressed. And return true
setPressed(true, x, y);
break;
case MotionEvent.ACTION_UP:
//Remember our last article was about clicking events for example. An ACITON_DOWN and an ACTION_UP
//When the ACTION_UP event is also passed in through the above process. This method is implemented.
//This method actually tells OnClickListener that there is a click event. Then return true
//See the source code for performClick () below.
performClick();
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
}
return true;
}
return false;
}
At this point, someone might say that every time I want to listen for clickable events, I have to set up clickable and long_clickable first. At this time, we still use the source code to speak.
//As you can see, the android source code automatically sets Clickable (true) and
//SetLong Clickable (true), so we don't have to worry too much about whether we set these two properties when we listened for click events before.
public void setOnClickListener(@Nullable OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
getListenerInfo().mOnClickListener = l;
}
public void setOnLongClickListener(@Nullable OnLongClickListener l) {
if (!isLongClickable()) {
setLongClickable(true);
}
getListenerInfo().mOnLongClickListener = l;
}
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
//Determine whether OnClickListener is set for the current View.
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
//If OnClickListener is set, call back the onClick () method directly.
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
OK, so far, the distribution and processing of a click event, we will complete the analysis through the source code.
Nevertheless, the issue of event distribution is not only that, but also more in-depth issues awaiting our discussion.
If you have any questions, please correct them.