View 事件分发(csdn)
- 传递图示
- 事件拦截的应用场景
- 面试题
- 为什么要有事件传递机制
- 因为android的view 是树形结构,当点击事件发生的时候 要经过好几层的View ,若是没有事件传递机制 ,就没办法确定这个事件给谁处理了,所以事件传递机制就是为了处理这个问题而出现的
- android布局加载的过程
- 用户执行Activity的setContentView方法,内部是PhoneWindow的setContentView方法,在PhoneWindow中完成DecorView的创建,
- PhoneWindow是window的实现类.
- DecorView是Activity的根View,也是PhotoWindow的内部类,并且继承了Framlayout.
- DecorView将屏幕分为2个部分:titleView和contentView,我们平常加载的布局就是ContentView.
- android 事件传递的三个重要方法
- viewgroup 拥有这三个方法 acitivity和view没有拦截方法,
- viewgroup 拥有这三个方法 acitivity和view没有拦截方法,
- 事件分发的流程
- Activity——>PhoneWindow——>DecorView——>ViewGroup——>...——>View
- Activity——>PhoneWindow——>DecorView——>ViewGroup——>...——>View
- onTouchListener,onTouchEvent和onClick的优先级别
- onTouchListener—–>onTouchEvent—>onclick
- onTouchListener >>> onTouchEvent >>> setOnLongClickListener >>> OnClickListerner
- 总结
- onTouch的优先级高于onClick
- 控件被点击时,onTouch返回false—>dispatchTouchEvent方法返回false—>执行onTouchEvent—>在performClick方法里回调onClick
- onTouch返回true—>dispatchTouchEvent方法返回true—>不执行onTouchEvent,显然onClick方法也不会被调用
- 参考https://juejin.im/entry/58df5b33570c35005798493c
- 面试口述
- onTouchListener 的优先级要高于onTouchEvent ,若onTouchListener返回的是true onTouchEvent 就不执行了
- View中为什么没有onInterceptTouchEvent事件拦截方法?
- View最为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截
- 用伪代码表示ViewGroup的事件分发过程并解释?
- 对于一个ViewGroup来说,点击事件产生后,首先会传递给它,这时她的dispatchTouchEvent会被调用,如果这个ViewGroup的onInterceptTouchEvent,方法返回true表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent就会被调用;如果这个这个ViewGroup的onInterceptTouchEvent,方法返回false就表示它不拦截当前事件,这时事件就会传递给子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直到事件最终被处理。
- 常见的注意问题
- 如果事件一直没有被消费,最后会传给Activity,如果Activity也不需要就被抛弃。
- view事件执行顺序dispatchTouchEvent-> setOnTouchListener的onTouch->onTouchEvent,如果setOnTouchListener返回ture,后续事件onTouchEvent不在执行
- onClick是在onTouchEvent(event)方法中的,所以onTouch优先于onClick执行
- 如果重写dispatchTouchEvent方法,dispatchTouchEvent无论返回true还是false,事件都不再进行分发, 只有当其返回super.dispatchTouchEvent(ev),才表明其具有向下层分发的愿望。
- setOnLongClickListener的onLongClick的返回值表示什么?
- 返回false,长按的话会同时执行onLongClick和onClick;如果setOnLongClickListener返回true,表示事件被消耗,不会继续传递,只执行longClick;
- requestDisallowInterceptTouchEvent 可以在子元素中干扰父元素的事件分发吗?如果可以,是全部都可以干扰吗?
- 答:肯定可以,但是down事件干扰不了。
- 为什么要有事件传递机制
- 简述事件传递的流程
- 事件都是从Activity.dispatchTouchEvent()开始传递
- 一个事件发生后,首先传递给Activity,然后一层一层往下传,从上往下调用dispatchTouchEvent方法传递事件:
- activity --> ~~ --> ViewGroup --> View
- 如果事件传递给最下层的View还没有被消费,就会按照反方向回传给Activity,从下往上调用onTouchEvent方法,最后会到Activity的onTouchEvent()函数,如果Activity也没有消费处理事件,这个事件就会被抛弃:
- dispatchTouchEvent方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent进行判断是否拦截该事件。
- onTouchEvent方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。
- onInterceptTouchEvent是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的
- 上层View既可以直接拦截该事件,自己处理,也可以先询问(分发给)子View,如果子View需要就交给子View处理,如果子View不需要还能继续交给上层View处理。既保证了事件的有序性,又非常的灵活。
- 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递
- 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来
- 应用场景
- 常见的滑动冲突
- 外部滑动方向和内部滑动方向不一致的场景
- ViewPager和Fragment配合使用组成的页面滑动效果。这种冲突的解决方式,一般都是根据水平滑动还是竖直滑动(滑动的距离差)来判断到底是由谁来拦截事件
- 外部滑动方向和内部滑动方向一致的场景
- 内外两层同时能上下滑动或者能同时左右滑动。这种一般都是根据业务来进行区分。例如 ScrollView 和ListView 滑动的场景
- 外部滑动方向和内部滑动方向不一致的场景
- 滑动事件冲突解决方案
- 内部拦截法
- 外部拦截法
- 常见的滑动冲突
- 基于上述总结写了一个事件传递的例子
- 面试视角
- 参考