事件传递过程 Activity Window View
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
onUserInteraction
activity在分发各种事件的时候会调用该方法。 各种事件都会出发这个api
/** * Called whenever a key, touch, or trackball event is dispatched to the * activity. Implement this method if you wish to know that the user has * interacted with the device in some way while your activity is running. * This callback and {@link #onUserLeaveHint} are intended to help * activities manage status bar notifications intelligently; specifically, * for helping activities determine the proper time to cancel a notfication.
if (getWindow().superDispatchTouchEvent(ev)) { return true; }
Window的真正实现类是PhoneWindow 其实调用最后执行到decorview的
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
DecorView的dispatchTouchEvent方法
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }
最后调用ViewGroup的dispatchTouchEvent方法
所以真正的事件传递就是首先传递给Activity的onTouchEvent方法 随后就是遍历这个界面所有的view看看有没有某个view消费这个事件。
事件传递:View viewgroup phonewindow decorview activity
https://www.jianshu.com/p/90648dddd141
ViewGroup view的事件传递
eg:
// 注意事件在处理的时候 如果事件传递给下面的view没有任何一个处理
这个时候事件会给顶层 eg activity // 加入一个view消费这个事件 以后的事件都会交给他处理。 rela= (MyRelativeLayout) findViewById(R.id.rela); tv= (MyTextView) findViewById(R.id.tv); // 这个方法使用场景没有模拟出来 ????? 原因:这个方法调用位置不对
应该在dispatchTouchEvent事件里面进行处理 // 这个地方在调用在VG的down事件里面已经恢复了, tv.getParent().requestDisallowInterceptTouchEvent(true);
@Override public boolean dispatchTouchEvent(MotionEvent event) { Log.d(TAG, "dispatchTouchEvent: "); getParent().requestDisallowInterceptTouchEvent(true); return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { Log.d(TAG, "onTouchEvent: "); return true; // return super.onTouchEvent(event); }
// 1: 总结 当viewgroup拦截了事件 并且将事件处理了(就是onTouchEvent TouchListener
click只要有一个返回值是true 仅仅拦截不处理这个事件 // 还是会传递给父view来处理) // 这个流程中只有action_down事件会触发Viewgroup的 // onInterceptTouchEvent (VG的onInterceptTouchEvent在down事件一定会触发) // 随后就不会再次出发onInterceptTouchEvent事件 原因如下:想触发这个方法前提是:
actionMasked == MotionEvent.ACTION_DOWN // || mFirstTouchTarget != null down事件 或者mFirstTouchTarget
(就是事件有VG的子元素处理了 这个字view就会赋值给) // mFirstTouchTarget 所以此时这两个条件都不成立 // 2:子view处理事件 这个时候onInterceptTouchEvent每次都会执行 @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.d(TAG, "dispatchTouchEvent: "); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.d(TAG, "onInterceptTouchEvent: "); // if(ev.getAction()==MotionEvent.ACTION_DOWN){ // return super.onInterceptTouchEvent(ev); return false; }else{ return true; } } @Override public boolean onTouchEvent(MotionEvent event) { Log.d(TAG, "onTouchEvent: "); // return true; return super.onTouchEvent(event); }
结论性语言
dispatchTouchEvent
onTouchEvent
onInterceptTouchEvent
onTouchListener
onClickListener
1:正常情况下 一个时间序列只可以有一个view处理
2:
mFirstTouchTarget
这个就是要处理事件的目标对象,
3:当Vg拦截了down事件之后 以后所有的事件都会有他处理 另外以后再也不会触发onIntercept拦截事件的api
所以父view一定不可以拦截down事件。否则 子view以后就不可以获取事件。
4:
requestDisallowInterceptTouchEvent
子view调用这个方法将会是父view不能拦截除down之外的其他事件。
5:接下来寻找处理事件的view 过滤条件是1:不执行动画 2:在事件出发的区域内。
6:
dispatchTransformedTouchEvent
这个方法其实就是调用子元素的dispatchevent方法 另外最后VG也是调用View里面的dispatch方法 因为他调用的是super
调用的就是父class的api 所以就是view里面的方法。(归根结底)
onMeasure onDraw onLayout全面解析
7:
newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true;
赋值获取事件处理的view child
8:随后就是view里面对事件的处理流程。
9:自己需要再重新核对一下整体流程 梳理一下 自己写写看看
将事件处理这一块 写几个简单的demo试试。
10:事件传递流程梳理:核心点
1:onFilterTouchEventForSecurity(ev) 检测一下view是否处于最顶层 是否是被遮盖的
2: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(); }
action_down的时候 会强行清除一切状态和变量(所以我们在调用这个api
view.getParent().requestDisallowInterceptTouchEvent();对down事件是没有任何作用的)
3: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 = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } 随后执行事件拦截这一块流程 onInterceptTouchEvent 注意条件 MotionEvent.ACTION_DOWN mFirstTouchTarget != null (归纳几个点:我们在做时间拦截的时候 VG不可以将down时间拦截了,否则后期一切事件都会被VG处理:原因down事件被拦截 导致mFirstTouchTarget 不会被复制其他事件过来的时候就直接走else语句 intercepted变量一直会为true,事件就被拦截了) 4:两条路:被VG拦截 handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
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; } 注意此时child为null执行 handled = super.dispatchTouchEvent(event); 这个就是执行VG的父view的api 事件就直接传递给父view处理事件5:事件传递给子view处理:过滤条件:view没有执行动画 view位置在可点击区域 view处理了这个事件 主意这个地方是一个
递归逻辑 直到找到一个执行事件的view(也可能没有child 执行流程就回到的上面的流程)
找到之后:
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
private TouchTarget addTouchTarget(View child, int pointerIdBits) { TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target; return target; }给相关变量赋值 mFirstTouchTarget = target; 此次事件应该派遣给某个子view处理
疑问点随后解析:???
11:结论:
VG和VIEW事件处理区别关键就在 dispatchTouchEvent 事件分发流程和VG还多了一个事件拦截 onInterceptTouchEvent不一样,
onTouchEvent OnTouchListener onClickListener 其实最后都是调用的父view的方法
VG是VIEW的子类
12:View的 dispatchTouchEvent流程分析:
if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement 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; } }首先判断 是否调用我们的 OnTouchListener 前提条件是:1:存在这个listener 2:view是enable的
如果没有listener或者非enable的就执行onTouchEvent
13:view的onTouchEvent分析:
view的enable状态不影响onTouchEvent的api执行
CLICKABLE
LONG_CLICKABLE
为true就可以:
performClick();执行点击事件更为详细的分析????14:VG的
public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
系统默认都是返回false 父view不拦截事件:
事件传递的原则是:父view如果不主动拦截事件即便自己可以处理事件 也会先将事件传递给子view处理 当自己的子
view都不处理的时候,这个事件才会再次传递给父view 又父veiw来执行。