美文网首页
View的事件分发机制(下)

View的事件分发机制(下)

作者: FourStars | 来源:发表于2018-10-24 22:02 被阅读0次

    上回说到了触摸事件是如何在应用中进行传递的原理--责任链模式

    顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。
    在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推(源自菜鸟教程)

    现在从源码分析,事件是怎么一步一步处理的

    View--dispatchTouchEvent()分析

        /**
         * 传递触摸事件到指定的View中,这个View可以是自己本身
         *
         * @param event 需要被传递的事件
         * @return True 则事件被消耗,可以继续处理其他事件, false 则继续传递.
         */
        public boolean dispatchTouchEvent(MotionEvent event) {
            //代码
        }
    

    在dispatchTouchEvent中有一些测试代码和判断焦点代码,这里暂且忽略。

    Tips:onFilterTouchEventForSecurity中进行检测当前Window是否被遮挡,遮挡则不处理该触摸事件,则会继续传递下去

      public boolean dispatchTouchEvent(MotionEvent event) {
          //其他代码
          if (onFilterTouchEventForSecurity(event)) {
                //判断是否是鼠标滑动滚动条-->boolean handleScrollBarDragging()
                //获取监听管理信息
                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;
                }
            }
          //其他代码
          return result;//初始值是false
      }
    

    注意到,我们获取监听信息这个类之后,当所需条件不为Null
    即mListenerInfo不为Null,设置了监听器,View处于Enable(可用)状态才会回调onTouch来处理触摸事件
    因为用的是&&运算符,所以前三个任意一个不满足条件时,都走不到onTouch

    image.png

    而onTouch返回Ture的话,那么dispatchTouchEvent就到此为止,该触摸事件就被消耗完毕,可以继续下一个事件的传递了

    同样的onTouch也返回false的话,就可以继续回调onTouchEvent来处理,点击方法,发现这个特别长,我们忽略一些代码

    public boolean onTouchEvent(MotionEvent event) {
            //这里获取是否可点击的状态
            final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
            //当View不可用时,会返回可否点击的状态,意味着触摸事件会因Clickable为True被消耗掉
            if ((viewFlags & ENABLED_MASK) == DISABLED) {
                //其他代码
                return clickable;
            }
    
            if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
                switch (action) {
                    case MotionEvent.ACTION_UP:
      //处理事件
                    break;
                    case MotionEvent.ACTION_DOWN:
      //处理事件
                    break;
                    case MotionEvent.ACTION_CANCEL:
      //处理事件
                    break;
                    case MotionEvent.ACTION_MOVE:
      //处理事件
                    break;
                }
              }
        retrun true;
    }
    

    可以看到,当View可点击或者长按或悬停时能显示工具栏的时候,会对触摸事件做出响应

    综上可知各事件处理的顺序为
    onTouch() > onTouchEvent() > onLongClick() > onClick()

    Q:为什么onLongClick() > onClick()
    A:click需要两个动作Down&Up,而longClick只需要Down即可

    看上述代码可知,只要一个View是Clickable的,那么它回调onTouchEvent必定返回true,这意味着事件就被消耗啦

    ViewGroup--dispatchTouchEvent

    我们知道ViewGroup和View相比,多了一个拦截事件的方法-->onInterceptTouchEvent()
    点击ViewGroup查看dispatchTouchEvent,我们发现和View相比多了些东西

            if (onFilterTouchEventForSecurity(ev)) {
                //其他代码
                // 检查是否拦截事件
                final boolean intercepted;
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || mFirstTouchTarget != null) {
                    //该参数在java文件中为ViewGroup设置-->requestDisallowInterceptTouchEvent(boolean disallowIntercept )
                    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                    if (!disallowIntercept) {
                        intercepted = onInterceptTouchEvent(ev);
                        ev.setAction(action);
                    } else {
                        intercepted = false;
                    }
                } else {
                    intercepted = true;
                }
              
              final boolean canceled = resetCancelNextUpFlag(this)
                        || actionMasked == MotionEvent.ACTION_CANCEL;
              if (!canceled && !intercepted) {
                     //分发到子View
              }
            }
    

    正如上述代码所述,当intercepted为ture即触摸事件被拦截时,ViewGroup不会分发事件到子View中

    总结

    自己看懂和自己写出来总是不一样的,但是行动了总没有错,虽然我写的乱七八糟...

    相关文章

      网友评论

          本文标题:View的事件分发机制(下)

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