##一,事件传递机制产生原因:
1.为什么要有事件分发机制?
    因为安卓里的View是树形结构,View可能会重叠在一起,当我们点击的地方有多个View的时候,就出现滑动冲突了,事件应该分发给谁呢?这时候就有了事件分发机制。

 

 

 

面试题之----事件分发机制

##二,事件的传递机制详解

  (一) 布局加载过程:

     1,,Activity中的setContentView()调用的是getWindow.setContentView(layoutResId0)方法,(getWindow()得到的是一个photoWindow对象).

     2,DecorView是Activity的跟View,也是PhotoWindow的内部类,并且继承了Framlayout.

     3,DecorView将屏幕氛围2个部分:titleView和contentView,我们平常加载的布局就是ContentView.

  (二),三个方法

    dispatchTouchEvent(分发事件)
    onInterceptTouchEvent(拦截事件)
    onTouchEvent(处理触摸事件)
    共同点:都接收一个MotionEvent作为参数,返回值类型都是boolean。

 

 

    1,dispatchTouchEvent()定义在Activity、ViewGroup和View中,控制TouchEvent走向,其目的就是找出哪个View应该处理该TouchEvent,简言来说,就是用于事件的分发。
      a,原则:自上而下分发,采用逐层负责制,最先收到的是Activity;

 

 

             分发之前先判断是否拦截

 

     b,每个ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在onInterceptTouchEvent方法中 return true就会交给自己的onTouchEvent的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent传递

 

    2,onInterceptTouchEvent()定义中ViewGroup中,用于拦截事件.返回true表示拦截;

        a,view是么有onInterceptTouchEvent()方法的,因为view不能包含view,所以不需要判断是否拦截 

    3,onTouchEvent(),定义在Activity、ViewGroup和View中,用来处理触摸事件.

        a,对于onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。

- (三),四个触摸事件
    Down,move,up,cancel

- (四),拥有能力处理事件传递的3个类
    Activity,ViewGroup,View
    3.1,View是树形结构的,所以分发就有顺序.事件收集之后最先传给Activity,然后依次向下传递,最后传入到底层的View

 

    Activity->photoWindow-DecorView->ViewGroup->.....->View

 

 

- (五),事件的传递过程

 

ViewGroup的事件传递方法:

  • dispatchTouchEvent
  • onInterceptTouchEvent
  • onTouchEvent

View的事件传递方法:

  • View的dispatchTouchEvent
  • View的onTouchEvent

      当点击事件产生时,Activity会调用dispatchTouchEvent()方法;

      当然具体的事物都是讲给PhotoWindow来完成;

     PhotoWindow再把事件交给DecorView完成,(DecorView是继承Fraglayout,处于ViewGroup);

     所以,最后DecorView会将事件处理工作交给ViewGroup;

   

     1,父容器拿到事件,不拦截,事件就分发给孩子,如果孩子消费事件,事件传递结束

    2,父容器拿到事件,不拦截,事件就分发给孩子,如果孩子不消费事件,事件又传递给父容器,看父容器是否消费(onTouchEvent),事件传递结束

    3,父容器拿到事件,拦截,事件就不会分发给孩子,交给自己处理(onTouchEvent),事件传递结束

   4,如果ACTION_DOWN事件没有view消费,那么后续的事件ACTION_MOVE,ACTION_UP就不会传递进来,如果ACTION_DOWN消费事件了,那必然会以一个Up或Cancel事件结束


- (六),注意:

 

    1,OnTouchListner的优先级高于onTouchEvent,如果OnTouchListner返回true,onTouchEvent就不执行,反之,则会调用

    2,如果事件一直没有被消费,最后会传给Activity,如果Activity也不需要就被抛弃。

    3,判断事件是否被消费是根据返回值,而不是根据你是否使用了事件。

##,onTouchListener,onTouchEvent和onClick的优先级别

     onTouchListener—–>onTouchEvent—>onclick

##优先级别,可以参考一下文章:

      https://blog.csdn.net/fenganit/article/details/53750265

       https://blog.csdn.net/huiguixian/article/details/22193977

##三,滑动冲突的解决方式:
之前的所有讲解,就像是所有的招式,滑动冲突,就是我们的用武之地。
    外部拦截法和内部拦截法
- (一),外部拦截法:就是在ViewGroup里使用onInterceptTouchEvent()拦截
- (二),内部拦截法:在子View的dispatchTouchEvent()里调用 ,这行代码被调用,父类就不会拦截事件

    当传入的参数为true时,表示子组件要自己消费这次事件,告诉父组件不要拦截(抢走)这次的事件
    getParent().requestDisallowInterceptTouchEvent(true);

-----------------------外部拦截在代码中的使用---------------------------------
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN :
                mDownX = ev.getX();
                mDownY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE :

                mMoveX = ev.getX();
                mMoveY = ev.getY();

                float dx = mDownX - mMoveX;
                float dy = mDownY - mMoveY;
                if(dx > dy) {
                    return true;//只拦截左右滑动
                }
                break;
        }

        return false;
    }

---------------------内部拦截在代码中的使用-----------------------------------
    public boolean dispatchTouchEvent(MotionEvent ev) {
    int x = (int) ev.getX();
    int y = (int) ev.getY();
    switch (ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        case MotionEvent.ACTION_MOVE:
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            if (父容器需要当前点击事件) {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
        default:
            break;
    }
    mLastX = x;
    mLastY = y;
    return super.dispatchTouchEvent(ev);
}

##四,总结
    View的事件分发机制实际上就是一个经典的责任链模式,
    责任链模式:当有多个对象均可以处理同一请求的时候,将这些对象串联成一条链,并沿着这条链传递修改请求,直到有对象处理它为止。

相关文章: