To better understand the event distribution mechanism ViewGroup, we have a MyLinerLayout in custom.
public class MyLinearLayout extends LinearLayout { private static final String TAG = MyLinearLayout.class.getSimpleName(); public MyLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP"); break; default: break; } return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "onTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "onTouchEvent ACTION_UP"); break; default: break; } return super.onTouchEvent(event); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "onInterceptTouchEvent ACTION_UP"); BREAK ; default : BREAK ; } return Super .onInterceptTouchEvent (EV); } @Override public void requestDisallowInterceptTouchEvent ( Boolean disallowIntercept) { Log.e (the TAG, "requestDisallowInterceptTouchEvent" ); Super .requestDisallowInterceptTouchEvent (disallowIntercept); } }
click when Button you can see the print information:
E/MyLinearLayout(959): dispatchTouchEvent ACTION_DOWN
E/MyLinearLayout(959): onInterceptTouchEvent ACTION_DOWN
E/MyButton(959): dispatchTouchEvent ACTION_DOWN
E/MyButton(959): onTouchEvent ACTION_DOWN
E/MyButton(959): onTouchEvent ACTION_MOVE
E/MyLinearLayout(959): dispatchTouchEvent ACTION_MOVE
E/MyLinearLayout(959): onInterceptTouchEvent ACTION_MOVE
E/MyButton(959): dispatchTouchEvent ACTION_MOVE
E/MyButton(959): onTouchEvent ACTION_MOVE
E/MyLinearLayout(959): dispatchTouchEvent ACTION_UP
E/MyLinearLayout(959): onInterceptTouchEvent ACTION_UP
E/MyButton(959): dispatchTouchEvent ACTION_UP
E/MyButton(959): onTouchEvent ACTION_UP
You can see the general flow of events is:
MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent
As can be seen, the trigger event on the View, the first to capture the event for ViewGroup View is located, and then only to the View itself.
Let's look at dispatchTouchEvent ViewGroup source:
The longer the code, look down at the event:
public boolean dispatchTouchEvent (Motion ev) { if (! onFilterTouchEventForSecurity (cv)) { return false ; } Final int action = ev.getAction (); Final float xf = ev.getX (); Final float yf = ev.getY (); Final float scrolledXFloat = xf + mScrollX; Final float scrolledYFloat = YF + mScrollY; Final Rect frame = mTempRect; boolean= disallowIntercept (mGroupFlags & FLAG_DISALLOW_INTERCEPT) = 0! ; IF (== Action MotionEvent.ACTION_DOWN) { IF (! mMotionTarget = null ) { // put down when the trigger is reset mMotionTarget null mMotionTarget = null ; } // if not allowing interception or not interception but allow intercepted inside will loop through View IF (! disallowIntercept || onInterceptTouchEvent (EV)) { // RESET apos the this Event Action (Just to Protect Ourselves) ev.setAction (MotionEvent.ACTION_DOWN); / / We know we want to dispatch the event down, find a child // who can handle it, start with the front-most child. final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View[] children = mChildren; final int count = mChildrenCount; for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system final float xc = scrolledXFloat - child.mLeft; final float yc = scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
// If you click View to find and return value == true, then mMotionTarget will be assigned to the current sub-View
// and returns true, down the end of the event distribution IF (child.dispatchTouchEvent (EV)) { // Event the Handled, . A target WE have have now mMotionTarget = Child; return to true ; } // of The Event DID Not the Handled GET, the try The Next View. // the Do Not RESET The Event apos LOCATION, IT apos Not // Necessary here Wallpaper. } } } } }
move source:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final float xf = ev.getX(); final float yf = ev.getY(); final float scrolledXFloat = xf + mScrollX; final float scrolledYFloat = yf + mScrollY; final Rect frame = mTempRect; boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; //获取down事件时的mMotionTarget finalTarget = View mMotionTarget; // allow intercepted and blocked IF (disallowIntercept &&! OnInterceptTouchEvent (EV)) { // source placed below speak intercepted } Final a float XC = scrolledXFloat - ( a float ) target.mLeft; Final a float YC = scrolledYFloat - ( float ) target.mTop; ev.setLocation (XC, YC); // will not intercept method calls the sub-view distribution of return target.dispatchTouchEvent (EV); }
By the same token up event, not put up .
1, ACTION_DOWN in, ViewGroup event has been captured, and then determine whether to intercept, if not intercepted, it is found containing the current x, sub View y coordinates assigned to mMotionTarget, then call mMotionTarget.dispatchTouchEvent
2, ACTION_MOVE in, ViewGroup event has been captured, and then determine whether to intercept, if not intercepted, direct call mMotionTarget.dispatchTouchEvent (ev)
3, ACTION_UP in, ViewGroup event has been captured, and then determine whether to intercept, if not intercepted, direct call mMotionTarget.dispatchTouchEvent (ev)
About ViewGroup interception methods:
public boolean onInterceptTouchEvent (MotionEvent EV) { int Action = ev.getAction (); Switch (Action) { Case MotionEvent.ACTION_DOWN: // If you feel the need to intercept the return to true ; Case MotionEvent.ACTION_MOVE: // If you feel the need to intercept return to true ; Case MotionEvent.ACTION_UP: // If you feel the need to intercept the return to true ; } return false ; }
The default is not blocked, that return false; if you want to intercept, just return true on the line, this event would not have to be the child View passed, and if you are true in DOWN retrun,
Then DOWN, MOVE, UP sub-View will not capture the event; if you MOVE return true, then the child will not be captured in View MOVE UP and events
The reason is simple, when onInterceptTouchEvent (ev) return true, the set will mMotionTarget null;
return true if ViewGroup of onInterceptTouchEvent (ev) when ACTION_MOVE, that is intercepted child View of MOVE UP and events;
At this time, still hope to respond to child View MOVE UP and the time we supposed to do?
Android provides us with a method: requestDisallowInterceptTouchEvent (boolean) is used to set whether to allow the interception, so write us directly in the sub-View of dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) { getParent().requestDisallowInterceptTouchEvent(true); int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: Log.e(TAG, "dispatchTouchEvent ACTION_DOWN"); break; case MotionEvent.ACTION_MOVE: Log.e(TAG, "dispatchTouchEvent ACTION_MOVE"); break; case MotionEvent.ACTION_UP: Log.e(TAG, "dispatchTouchEvent ACTION_UP" ); BREAK ; default : BREAK ; } return Super .dispatchTouchEvent (Event); }
getParent () requestDisallowInterceptTouchEvent (to true);.
Thus, even if the MOVE ViewGroup when return true, the sub-View can capture still and MOVE UP event.
Look ViewGroup the Move Up and interception Source:
if (!disallowIntercept && onInterceptTouchEvent(ev)) { final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { // target didn't handle ACTION_CANCEL. not much we can do // but they should have. } //at The target the Clear mMotionTarget = null ; // the Do not dispatch the this Event to View Our own, already Because WE // the SAW IT the when the Intercepting; WE want to give the Just at The following // Event at The Normal to onTouchEvent (). return to true ; }
when we disallowIntercept set to true,! disallowIntercept directly to false, so the intercept method body is skipped, and also ineffective.
The above processes are normal distribution processes, if not find a suitable sub-View, or child View of dispatchTouchEvent returns false how to do?
This is part of the code above down event
IF (child.dispatchTouchEvent (EV)) {
mMotionTarget = Child;
return to true;
}
only to return in child.dispatchTouchEvent (ev) true, and will think you have found View capable of handling the current event, namely mMotionTarget = child, otherwise still null
Final View target = mMotionTarget; IF (target == null ) { // We do Not have have target A, the this means WE're Handling The // Event AS A Regular View. ev.setLocation (XF, YF); IF ( (mPrivateFlags & CANCEL_NEXT_UP_EVENT) = 0! ) { ev.setAction (MotionEvent.ACTION_CANCEL); mPrivateFlags & = ~ CANCEL_NEXT_UP_EVENT; } return Super .dispatchTouchEvent (EV); }
we do not have a target element can handle the event, means that we need deal with their own
to sum up:
1. If ViewGroup found View can handle the event, the processing directly to the sub-View, their onTouchEvent not be triggered.
2. by replication onInterceptTouchEvent (ev) method, child View intercept events (ie, return true), the event to deal with their own, will own the corresponding method execution onTouchEvent
3. sub-View by calling getParent () requestDisallowInterceptTouchEvent (true); ViewGroup prevent or intercept its MOVE UP events;