美文网首页
view事件分发机制

view事件分发机制

作者: 30cf443c3643 | 来源:发表于2018-10-12 15:26 被阅读6次

    view的事件分发过程由3个重要的方法完成

    • public boolean dispatchTouchEvent
      用于事件的分发。如果事件能传递到当前view,则该方法一定会调用。返回表示是否消耗当前事件
    • public boolean onInterceptTouchEvent
      用于判断是否拦截某个事件。返回表示是否拦截当前事件
    • public boolean onTouchEvent
      处理点击事件,是否消耗当前事件

    三者的关系用伪代码表示 先去询问是否拦截事件,拦截调用自己的事件处理方法,否则调用子view的分发方法

    public boolean dispatchTouchEvent(MotionEvent ev){
         boolean resume = false;
         if(onInterceptTouchEvent){
              consume = onTouchEvent(ev);
         }else{
               consume = child.dispatchTouchEvent(ev);
        }
        return resume;
    }
    

    事件的流程

    点击事件产生后,事件最先传递到Activity,由activity来派发事件(activity没有拦截事件的方法),具体的工作由内部的phoneWindow来完成,window又会传递给decorview,decorview是setcontview所设置的view的父容器。decorview然后传给viewgroup。viewgroup分发给view。然后view处理。如果view没有消耗掉事件,事件会反方向回传,最终传回给Activity。由此构成了一个经典的责任链模式的机制

    005Xtdi2jw1f88i0q8uozj30nm0kqwhm.jpg

    图中如果view1的onTouchEvent返回true,则通过dispatchTouchEvent告诉上级事件被消耗了

    事件的一些规则

    1. 某个view一旦拦截,那么它的拦截方法不会再被调用。同一序列的事件均由该view处理
    2. 某个view一旦开始处理,那么ACTION_DOWN事件必须消耗,否则同一事件序列里的其他事件将由父容器来处理了。
    3. view没有拦截方法,传递到view,那么onTouchEvent必会调用
    4. view的onTouchEvent默认会消耗事件,除非他是不可点击的clickable和longclickable均为false
    5. view的enable属性不影响onTouchEvent返回
    6. 通过requestDisallowInterceptTouchEvent可以在子元素中干预父元素的分发过程,但是ACTION_DOWN事件除外
    7. View的事件的调度顺序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener
    8. ViewGroup默认不拦截

    requestDisallowInterceptTouchEvent

    getParent().requestDisallInterceptRouchEvent(true) 来设置标志位FLAG_DISALLOW_INTERCEPT,让父布局禁用拦截事件功能。调用的要求是子View必须能拿到点击事件。比如父容器的拦截事件中把所有的Action都拦截了,那么子view拿不到事件。ViewGroup在遇到down事件时,会重置FLAG_DISALLOW_INTERCEPT标志位,也总会调用拦截方法来询问自己是否要拦截事件
    别人的案例。第一种情况是子View的down事件处理中调用了requestDisallowInterceptTouchEvent,设置了标志位,禁用了父容器的拦截方法,所以父容器拦截事件没有调用。第二种父容器拦截down事件,所以参看规则1,后续不再调用拦截方法。所以子view根本拿不到事件

    滑动冲突

    外部拦截法,通过重写父容器的拦截方法。父容器需要当前事件时拦截。外部拦截法,父容器都不拦截,子View配合requestDisallowInterceptTouchEvent

       public boolean dispatchTouchEvent(MotionEvent ev){
             switch(ev.getAction){
                     case DOWN:
                            parent.requestDisallowInterceptTouchEvent(true);//禁用父容器的需要拦截方法
                            break;
                     case MOVE:
                           if(父容器需要事件){
                                       parent.requestDisallowInterceptTouchEvent(false);
                            }
                           break;
            }
            return super.dispatchTouchEvent(ev);
       }
    

    相关文章

      网友评论

          本文标题:view事件分发机制

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