activity的分发
从activity的dispatchTouchEvent方法进行源码分析:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
onUserInteraction是个空方法,应该是实现屏保功能,当activity位于栈顶时,触屏点击home、menu、back会触发。
接下来的superDispatchTouchEvent是个是抽象方法,对应的Window也是个抽象类,我们能找到的PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
这个decor也就是 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks
也就是事件传递到了viewgroup。
activity·的ontouch方法
/**
* Called when a touch screen event was not handled by any of the views
* under it. This is most useful to process touch events that happen
* outside of your window bounds, where there is no view to receive it.
*
* @param event The touch screen event being processed.
*
* @return Return true if you have consumed the event, false if you haven't.
* The default implementation always returns false.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) {
finish();
return true;
}
return false;
}
这里默认返回false,就是不处理,向下层分发,在这个判断里面
/** @hide */
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
&& isOutOfBounds(context, event) && peekDecorView() != null) {
return true;
}
return false;
}
这里主要判断是否在边界外,在即消费事件,返回true,分发结束,反之返回false,activity层的分发也结束,扔给viewgroup继续分发,直到被消费。
Viewgroup的分发机制
viewgroup的dispatchTouchEvent中
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 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();
}
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {//如果是down事件,或者已经有子view会处理,那就要判断是否拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {//非down事件,而且这个事件是由自己处理,那肯定是拦截
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}
// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;
// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
// If the event is targeting accessiiblity focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;
// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);
// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
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);//在这里给mFirstTouchTarget赋值
alreadyDispatchedToNewTouchTarget = true;//只在这里赋值,说明,如果它是true,那么这个事件一定是true
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
主要是判断事件没有取消也没有拦截,然后遍历viewgroup里的子view
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;
————————————————
版权声明:本文为CSDN博主「月薪低于10k不改名」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38703938/article/details/81906880
dispatchTransformedTouchEvent方法中
/**
* 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;
// 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 (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
这里进行了判断,事件传递给了子view进行分发,或者给他的上一层viewgroup进行分发,直到时间分发结束,即被消费
拦截的判断
// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {//如果是down事件,或者已经有子view会处理,那就要判断是否拦截
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {//非down事件,而且这个事件是由自己处理,那肯定是拦截
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
mFirsTouchTarget!=null 不成立的话不会进行拦截
disallowIntercept是表示是否允许被拦截,可以通过子view的getParent
根据代码我们知道只要我们通过setOnClickListener()为控件View注册1个点击事件,那么就会给mOnClickListener变量赋值(即不为空),则会往下回调onClick() & performClick()返回true。即调用onTouch事件要调用performClick事件,当这些执行完才能执行我们常见的onClick事件,至此,事件分发结束。
————————————————
版权声明:本文为CSDN博主「月薪低于10k不改名」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38703938/article/details/81906880
View的事件分发
根据代码我们知道只要我们通过setOnClickListener()为控件View注册1个点击事件,那么就会给mOnClickListener变量赋值(即不为空),则会往下回调onClick() & performClick()返回true。即调用onTouch事件要调用performClick事件,当这些执行完才能执行我们常见的onClick事件,至此,事件分发结束。
结论
Viewgroup有dispatchTouchevent,intercepttouchevent,touchevent/view有dispatchTouchevent,touchevent
Activity收到点击事件,调用dispathTouchEvent(),随后会调用phonewindow. superDispatchTouchEvent,再调用DecorView.superDispatchTouchEvent,随后调用到自身的dispathTouchEvent方法,(注意:该方法调用流程是先判断点击事件是否是Action_down|| mFirstTouchTarget!=null,该值是判断是否有手指正在该view上)随后再判断disallowIntercept是否为false,满足条件则调用interceptTouchevent方法,如果interceptTouchEvent返回true,则执行当前viewGroup的touchEvent方法,如果返回false,则执行他子view的dispatchTouchEvent方法。如果touchEvent返回true则消耗此事件,如果返回false,则执行parentView的touchEvent事件
##事件分发
###Activity接收事件过程
1. InputManagerSerivce(IMS)负责接收并处理硬件发出的点击触摸等信息,将事件交给WMS。
2. WMS负责将消息分发给指定的Window。
3. app进程中InputEventReceiver负责接收触摸信息。
4. ViewRootImpl调用view.dispatchPointerEvent方法。
5. 调用decorView的dispatchTouchEvent
6. 调用Window的dispatchTouchEvent
7. 调用ActivityTouchEvent
IMS-->WMS-->InputEventReceiver-->ViewRootImpl-->View(dispatchPointerEvent)-->decorView(dispatchTouchEvent)-->(window)dispatchTouchEvent-->(Activity)dispatchTouchEvent
### dispatchTouchEvent
dispatchTouchEvent负责事件分发。ViewGroup中的dispatchTouchEvent方法只有调用super才能继续往下分发,返回true和false,都会再往下分发。
View的dispatchTouchEvent的用处是是否分发事件到自己。
![blockchain](https://upload-images.jianshu.io/upload_images/5319985-1539bdeb0d29d8d1.png "事件分发")
ViewGroup和View的dispatchTouchEvent方法返回false代表字View/ViewGroup不处理事件,交给父View处理,调用父View的onTouchEvent。Activity因为没有父View了,返回true和false都会自己消费。
https://www.jianshu.com/p/d82f426ba8f7
<B>!!!!加粗,ACTION_UP事件的传递流程,看上图中标注消费的地方,最后哪里消费了ACTION_UP就传递到哪层。</B>
例如结构是:
Activity-->ViewGroup1-->ViewGroup2-->View
场景1:VG1 dispatch方法中Down返回true,down事件在此处消费,不会分发到VG2和View,onTouchEvent的down不会响应,VG1的dispatch和onTouch的up事件则可以响应到。
场景2:VG1 onInterceptTouchEvent中ACTION_DOWN返回true,标识要拦截down事件,会回调onTouchEvent Down,如果onTouchEvent的down返回true,表示事件在此处消费,ACTION_UP会传递到这里。
onInterceptTouchEvent不会消费事件,dispatchTouchEvent和onTouchEvent返回true的时候才会消费时间,
onTouch和onTouchEvent的区别
onTouch通过setOnTouchListener来设置View触摸的回调,在View的dispatchTouchEvent中会判断onTouch方法的返回值,如果onTouch返回true则等同于dispatchTouchEvent返回true。例如VG1设置了onTouchListener切onTouch返回true,则VG1的onTouchEvent down不会响应,VG1的dispatchTouch会影响到UP事件,onTouchEvent的UP不会响应(因为onTouch返回true==dispatchTOuchEvent返回true)