View的事件分发机制--笔记

作者: Kevin_Curry | 来源:发表于2016-03-16 10:31 被阅读653次

    传递顺序:

    Activity-->phoneWindow-->DecorView-->子View

    public boolean dispatchTouchEvent(MotionEvent ev);  用来进行事件分发表示是否消耗当前事件

    public boolean onInterceptTouchEvent(MotionEvent ev);  判断是否拦截某个点击事件 返回true就拦截 false不拦截向下传递 ;

    public boolean onTouchEvent(MotionEvent ev);   用来处理点击事件

    当点击屏幕后会调用ViewGroup的dispatchTouchEvent

    在这个方法中会对触摸事件进行判断 类型DOWN MOVE UP

    比如是点击事件 会在这个方法中判断当前是否拦截

    怎么判断是否拦截onInterceptTouchEvent

    true则拦截 false 不拦截

    如果拦截则调用ViewGroup的onTouchEvent方法

    如果不拦截则遍历子View根据当前点击坐标传递给子View分发给子View;

    如何不拦截 子View调用requestDisallowInterceptTouchEvent(boolean)

    分发给子View后 子View调用dispatchTouchEvent 会判断mOnTouchListener是否为null

    和View是否为enable状态还有mOnTouchListener.onTouch是否为true

    这三个条件如果都满足,直接return true 表示事件被消费; 也就是下面的onTouchEvent(event)不会被执行了;

    如果onTouchEvnet执行则会在onTouchEvent中判断触摸事件类型DOWN MOVE UP

    View的事件分发 和ViewGroup的事件分发

    View的事件分发

    不管是DOWN,MOVE,UP都会按照下面的顺序执行:

    1、dispatchTouchEvent

    在该方法中会去判断mOnTouchListener不为null,并且view是enable的状态 OnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ;

    2、 setOnTouchListener的onTouch

    如果我们设置了setOnTouchListener,并且return true,那么View自己的onTouchEvent就不会被执行了

    3、onTouchEvent

    如果我们的View可以点击或者可以长按,则,注意IF的范围,最终一定return true ;

    1、MotionEvent.ACTION_DOWN

    当用户按下,首先会设置标识为PREPRESSED

    如果115后,没有抬起,会将View的标识设置为PRESSED且去掉PREPRESSED标识,然后发出一个检测长按的延迟任务,延时为:ViewConfiguration.getLongPressTimeout() - delayOffset(500ms -115ms),这个115ms刚好时检测额PREPRESSED时间;也就是用户从DOWN触发开始算起,如果500ms内没有抬起则认为触发了长按事件:

    75行:给mPrivateFlags设置一个PREPRESSED的标识

    76行:设置mHasPerformedLongPress=false;表示长按事件还未触发;

    77行:发送一个延迟为ViewConfiguration.getTapTimeout()的延迟消息,到达延时时间后会执行CheckForTap()里面的run方法:

    2、MotionEvent.ACTION_MOVE

    拿到当前触摸的x,y坐标;

    判断当然触摸点有没有移出我们的View,如果移出了:

    1、执行removeTapCallback();

    2、然后判断是否包含PRESSED标识,如果包含,移除长按的检查:removeLongPressCallback();

    3、最后把mPrivateFlags中PRESSED标识去除,刷新背景;

    3、MotionEvent.ACTION_UP

    总结

    1、整个View的事件转发流程是:

    View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

    在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。

    DOWN时:

    a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;

    b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;

    c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:

    此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

    MOVE时:

    主要就是检测用户是否划出控件,如果划出了:

    115ms内,直接移除mPendingCheckForTap;

    115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

    UP时:

    a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;

    b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;

    c、如果是500ms以后,那么有两种情况;

    i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;

    ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;

    d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;

    ViewGroup的事件分发

    可以看到大体的事件流程为:

    MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent 

    可以看出,在View上触发事件,最先捕获到事件的为View所在的ViewGroup,然后才会到View自身~~

    26行:进行判断:if(disallowIntercept || !onInterceptTouchEvent(ev))

    两种可能会进入IF代码段

    1、当前不允许拦截,即disallowIntercept =true

    2、当前允许拦截但是不拦截,即disallowIntercept =false,但是onInterceptTouchEvent(ev)返回false ;

    注:disallowIntercept 可以通过viewGroup.requestDisallowInterceptTouchEvent(boolean);进行设置,后面会详细说;而onInterceptTouchEvent(ev)可以进行复写。

    36-57行:开始遍历所有的子View

    41行:进行判断当前的x,y坐标是否落在子View身上,如果在,47行,执行child.dispatchTouchEvent(ev),就进入了View的dispatchTouchEvent代码中了

    总结一下

    onInterceptTouchEvent 是ViewGroup提供的方法,返回false不拦截,返回true表示拦截 默认false。 

    ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行分发工作,或者说它分发的对象就是自己,决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件

    ViewGroup的onTouchEvent事件是什么时候处理的呢?当ViewGroup所有的子View都返回false时,onTouchEvent事件便会执行。由于ViewGroup是继承于View的,它其实也是通过调用View的dispatchTouchEvent方法来执行onTouchEvent事件。

    假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该ViewGroup的所有下发操作拦截掉

    onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。

    ViewGroup实现捕获到DOWN事件,如果代码中不做TOUCH事件拦截,则开始查找当前x,y是否在某个子View的区域内,如果在,则把事件分发下去。

    如果没有被设置那么在onTouchEvent中如果设置了mOnClickListener 则onClick会被调用。如果顶级viewGroup不拦截事件则事件会被传递到事件链的子view这时候子view的dispatchTouchEvent会被调用到此为止 事件已经从顶级view传递给了下一级view 接下来的传递过程和顶级view是一致的如此循环完成整个事件的分发。

    如何拦截?

    复写ViewGroup的onInterceptTouchEvent方法:
    默认是不拦截的,即返回false    如果你需要拦截,只要return true这样该事件就不会往子View传递了

    相关文章

      网友评论

        本文标题:View的事件分发机制--笔记

        本文链接:https://www.haomeiwen.com/subject/emvgkttx.html