战锤40K:暗潮免安装中文联机版
78.5G · 2025-09-21
从Android事件分发机制看滑动冲突解决方案
事件分发机制从ViewGroup的dispatchTouchEvent入手
public boolean dispatchTouchEvent(MotionEvent ev) { ... final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { 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 = true; } if (!canceled && !intercepted) { ... TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } } ... } } }
可以看出,ViewGroup的dispatchTouchEvent,首先调用了自己的onInterceptTouchEvent方法,如果此方法返回true,则会短路后面的逻辑。进入下列逻辑
if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); }
然后调用
super.dispatchTouchEvent(event);
即View的方法,然后调用ViewGroup的onTouchEvent(event)方法
如果ViewGroup的onInterceptTouchEvent返回false则会调用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign),最终调用 handled = child.dispatchTouchEvent(event);到这里又是一个循环了
用图表示
那么,回到滑动冲突,就简单了。 外部拦截法 需要外部控件重写父类的onInterceptTouchEvent 方法,在其中判断什么时候需要拦截事件由自身处理,什么时候需要放行将事件传给内层控件处理,内部控件不需要做任何处理。伪代码如下:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: if (父容器需要自己处理改事件) { return true;//拦截 } else { return false;//不拦截 } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: break; } return super.onInterceptTouchEvent(ev); }
内部拦截法 这种方式需要结合 requestDisllowInterceptTouchEvent(boolean) 这个方法,外部不拦截的情况下,会调用此方法,在内层控件的重写方法dispatchTouchEvent中,根据逻辑来决定外层控件何时需要拦截事件,何时需要放行。(requestDisllowInterceptTouchEvent 这个方法的作用是-是否允许外层控件拦截事件)伪代码如下:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: if (父容器需要处理改事件) { //允许外层控件拦截事件 getParent().requestDisallowInterceptTouchEvent(false); } else { //需要内部控件处理该事件,不允许上层viewGroup拦截 getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: break; default: break; } return super.dispatchTouchEvent(ev); }
同时,在这种方法下需要在外层控件做下处理:
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { return false; } else { return true; } }
注:调用 getParent().requestDisallowInterceptTouchEvent(true)方法之后,父控件在dispatchTouchEvent的时候会直接将事件传递到子控件中
《艾尔登法环》祖灵成就怎么达成?祖灵成就解锁条件一览
凯翼昆仑 iHD 长续航版将于 9 月 26 日上市:CLTC 续航 201 公里,匹配 1.5T 插电混动系统