View的事件传递

来源:互联网 发布:女王升级数据2017 编辑:程序博客网 时间:2024/05/16 05:41

在很多的滑动控件嵌套的情况下经常会出现滑动事件冲突等等。
在自定义控件的时候,需要处理触摸、点击、滑动等事件,需要考虑父容器的这些事件的冲突问题。
如何学习?
要处理view的事件就必须详细了解事件传递的流程,虽然view在处理事件传递的机制围绕的只是三点:
1.dispatchTouchEvent:事件分发
2.onInterceptTouchEvent事件拦截
3.onTouchEvent事件处理
但是,其中具体是如何来进行处理和分发的,建议查看View和ViewGroup的源码,那是最详细的,这里只是做一个粗略的分析,而且当前的博客中很难能将源码中的逻辑完全详细的分析整理,其实也是因为这套机制错综复杂而又巧妙。
推荐两篇博客:
郭霖:
http://blog.csdn.net/guolin_blog/article/details/9097463/

鸿洋:
http://blog.csdn.net/lmj623565791/article/details/38960443

一、View的事件分发。
1.控件的Listener事件触发的顺序是先onTouch,再onClick。
2.控件的onTouch返回true,将会onClick事件没有了—阻止了事件的传递。返回false,才会传递onClick事件(才会传递up事件)

源码依据:
View的事件分发
1.dispatchTouchEvent();分发
2.onTouchListener–>onTouch方法
3.onTouchEvent()
4.onClickListener–>onClick方法

结论:
1.如果onTouchListener的onTouch方法返回了true,那么view里面的onTouchEvent就不会被调用了,顺序:
①dispatchTouchEvent
②onTouchListener如果返回false,进入③
③onTouchEvent

2.如果view为disenable,则:onTouchListener里面不会执行,但是会执行onTouchEvent(event)方法

3.onTouchEvent方法中的ACTION_UP分支中触发onclick事件监听onTouchListener–>onTouch方法返回true,消耗次事件。down,但是up事件是无法到达onClickListener.
onTouchListener–>onTouch方法返回false,不会消耗此事件

二、ViewGroup+View的事件分发
ViewGroup会依次传递给子view,最终达到View
1.dispatchTouchEvent()
2.onInterceptTouchEvent(); 拦截触摸事件
3.onTouchEvent();

1.先接触到事件的是父容器。
2.执行顺序:
①.dispatchTouchEvent–>
②.onInterceptTouchEvent
③.onTouchListener,如果返回false则执行④
④.onTouchEvent

源码:2108行

     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;                }

源码:2197行

     if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

源码:2520行

    dispatchTransformedTouchEvent(){         if (child == null) {//如果ViewGroup里面没有子控件就交给自己处理(就是一个纯粹的View)            handled = super.dispatchTouchEvent(event);            } else {            handled = child.dispatchTouchEvent(event);            }    }

这里写图片描述

注意:
事件传递,在dispatch中进行事件的分发:
(1)主动拦截:onInterceptTouchEvent

(2)主动监听:onTouchListener

(3)分发给子view

(4)通过判断当前view是否被消费,来找到tagetView:如果找到了,那么就根据是否有targetView来进行后面的处理:
①:down、move、up等事件的处理, 在targetView
的onTouchEvent中
②:最终的view没有消费的时候,并不是直接调用
super.onTouchEvent,而是返回没有targetView消费事件,体现了责任链模式。
抽象地讲,事件传递的确是按照U形来进行的,但是在这里,所有的事件分发都有dispatchTouchEvent来处理,责任单一。
如果没有找到targetView,下面的逻辑就会调用
super.dispatchTouchEvent将当前的事件分发转给父view(这里的名字是handler),再由父view来进行事件分发的处理。

(5)在逻辑中事件分发是一个U形结构,由最顶层的viewGroup依次将事件传递给子view(中间可以主动拦截或者主动监听),直到找到消费
的目标View,无论是否找到,都会依次返回到父view,由父View进行分发的处理。
但是在源码中,所有的view对象,都是由两个具体的类所实例化出来的(View、ViewGroup),所以事件传递都是在在这些类中进行递归调用。

Android源码工程师在事件传递上,表现的实在很高明:
①.单一责任原则,关键的事务所处理的事情不和其他耦合:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent
②.责任链模式,错综复杂的view中,最终消费事件的是目标view。分发的判断和目标都得到精简处理。

0 0
原创粉丝点击