The outline
- Event What events are distributed and what methods are involved
- How is event distribution passed from an Activity to a View
- View event distribution
- ViewGroup event distribution
1. Events What events are distributed and what methods are involved
- When the user touches the screen, an event is generated, as in Android
MotionEvent
There are many static constants in this class:
The name of the | value | meaning |
---|---|---|
ACTION_DOWN | 0 | The pressed gesture has started and the action contains the initial start position. |
ACTION_UP | 1 | After the gesture is pressed, the movement contains the final release position and all intermediate points since the last drop or move event |
ACTION_MOVE | 2 | A change has happened during a press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}) |
ACTION_CANCEL | 3 | The current gesture is aborted. |
. | . | . |
- By default, event distribution is distributed in the order from Activity to ViewGroup to View
- DecorView(mDecor is the View returned by getWindow().getDecorView(), and the View set by setContentView is a child of that View). Pass it to the Activity’s View tree.
1. Important methods of View event distribution:diapatchTouchEvent
andtouchEvent
.onClick
|---View
| |---boolean dispatchTouchEvent(MotionEvent event)
| |--- boolean onTouchEvent(MotionEvent event)
Copy the code
1.1 dispatchTouchEvent: True if the event is handled by the view, false otherwise.
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if(! isAccessibilityFocusedViewOrHost()) {return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if(mInputEventConsistencyVerifier ! =null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
// Check whether there is an event listEnter in listenerInfo and monTouchListener. onTouc returns true
ListenerInfo li = mListenerInfo;
if(li ! =null&& li.mOnTouchListener ! =null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// Second important point, if the above judgment is false and onTouchEventr returns true, consume the event
if(! result && onTouchEvent(event)) { result =true; }}if(! result && mInputEventConsistencyVerifier ! =null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if(actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_CANCEL || (actionMasked == MotionEvent.ACTION_DOWN && ! result)) { stopNestedScroll(); }return result;
}
Copy the code
- In the dispatchTouchEvent
final int actionMasked = event.getActionMasked();
- ListenerInfo:
view
All listener information is encapsulated in an object. - There are two judgments in this method:
ListenerInfo li = mListenerInfo;
if(li ! =null&& li.mOnTouchListener ! =null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if(! result && onTouchEvent(event)) { result =true;
}
Copy the code
If onTouchEvent or onTouchListenner is true, that event will be consumed, and the onTouchEvent will be done here
- boolean onTouchEvent
MotionEvent.ACTION_UP:
Call thePerformClick()
Three questions:
1. Called in the activity
mTouchView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG"."onTouch: " + event.getAction());
return false; }}); mTouchView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG"."onClick: "); }});/***********TabLayout************/
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG"."onTouchEvent: "+event.getAction());
return super.onTouchEvent(event);
}
Copy the code
Order of execution:
2. What if Tablyout returns ture directly?
Do not execute onClick because onClick is in ACTION_UP in view.onTouchEvent. If you return true, you will not use the method in super.
3. What if dispatchEvent return true?
Nothing
Distribution process
super.dispatchEvent->ListenerInfo->super.onTouchEvent(event)->ACTION_UP->performListener().
Copy the code
2. ViewGroup event distribution:
The first is actionDown:
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget ! =null) { //
final booleandisallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) ! =0;
if(! disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action);// restore action in case it was changed
} else {
intercepted = false; }}Copy the code
- MFirstTouchTarget: This is important, disallowIntercept is only called when pressed for the first time, this isa pit if you want to change the effect later.
|---ViewGroup
| |---dispatchTouchEvent
| | |---onInterceptTouchEvent(ev)
Copy the code
There are cases where the View consumes time (like click onTouchListener)
- Press for the first time to come in (down) – > ViewGroup. DispatchTouchEvent – > ViewGroup. OnInterceptTouchEvent (ev) — – > the dispatchEvent – (the view that a)
- In a second time (move) ViewGroup. DispatchTouchEvent – > ViewGroup. OnInterceptTouchEvent (ev) — – > the dispatchEvent – (the view that a)
- (up) for the third time) ViewGroup. DispatchTouchEvent – > ViewGroup. OnInterceptTouchEvent (ev) — – > the dispatchEvent – (the view that a) — — > the onC lick
There is no consumption event in the child View. Only walk again
- Come in for the first time (down) ViewGroup. DispatchTouchEvent – > ViewGroup. OnInterceptTouchEvent (ev) — – > the dispatchEvent – view. The onTouch – > onT ouch Event
ViewGroup source code analysis
actionDown
/** * Clears all touch targets. */
private void clearTouchTargets(a) {
TouchTarget target = mFirstTouchTarget;
if(target ! =null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while(target ! =null);
mFirstTouchTarget = null; / / remove target
}
}
<!--------------------------------------------------->
if(! canceled && ! intercepted)// intercepted defaults to no interception
{
if (newTouchTarget == null&& childrenCount ! =0) {
for (int i = childrenCount - 1; i >= 0; i--) {// The antisequence for loop takes the top layout first
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
}
}
}
}
Copy the code
/** * Transforms a motion event into the coordinate space of a particular child view, * filters out irrelevant pointer ids, and overrides its action if necessary. * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead. */
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);
handled = child.dispatchTouchEvent(event);
event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}
Copy the code