事件处理及解决事件冲突

来源:互联网 发布:淘宝主图 2017 编辑:程序博客网 时间:2024/06/15 21:23

事件传递原则:先捕获然后冒泡

  1. 在捕获阶段,事件先由外部的View接收,然后传递给其内层的View,依次传递到更够接收此事件的最小View单元,完成事件捕获过程;

  2. 在冒泡阶段,事件则从事件源的最小View单元开始,依次向外冒泡,将事件对层传递。

  3. Android中不同的控件所具有的事件分发、拦截和响应稍有不同,主要表现在Activity本身不具有事件拦截,不是ViewGroup的最小view单元也不具有事件拦截(因为它没有自己的子View)。

Android中事件分发

  1. 事件分发:dispatchTouchEvent(MotionEvent ev);自身可以消费事件返回(true)

    1. 当有事件的时候,先有当前的Activity捕获,
      • 如果返回true自身消费了,如果不想activity做任何事情可以直接返回true ;
      • 如果返回false交给上层的onTouchEvent消费时间,如果是activity的就直接交给系统消费
      • 如果返回super.dispatchTouchEvent(),
        1. 如果是activity交给ViewGroup的diapatchTouchEvent(),
        2. 如果是子View就给自身的onTouchEvent消费
        3. 如果是ViewGroup就交给自身的onInterceptTouchEvent()处理
  2. 事件拦截:只有ViewGroup拥有

    • 如果返回true表示拦截事件交给本层的onTouchEvent()处理事件
    • 如果返回false或者super.onInterceptTouchEvent(ev)都不拦截事件交给子View的dispatchTouchEvent()处理事件
  3. 事件处理:onTouchEvent();

    • 如果返回true表示事件由本层消费以后完结,不会再向上冒泡传递;
    • 如果返回false,或者默认的super.onTouchEvent()事件将在本层做处理后上层View冒泡传递给上层View的onTouchEvent()做处理
  4. 总结为:

    1. 从以上过程中可以看出,dispatchTouchEvent无论返回true还是false,事件都不再进行分发,

    2. 只有当其返回super.dispatchTouchEvent(ev),才表明其具有向下层分发的愿望,

    3. 但是是否能够分发成功,则需要经过事件拦截onInterceptTouchEvent的审核。事件是否具有冒泡特是由onTouchEvent的返回值决定的。

    4. 注意: ACTION_DOWN是跟着时间传递一起走的,而ACTION_MOVE与ACTION_UP

      • ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传;

      • 如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,三个传递是一样儿的;

      • 如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。

这里写图片描述

事件冲突的解决方法(如:pullToRefresh跟viewPager的冲突)

分两种情况: 外部拦截法 和内部拦截法

外部拦截法:

  • 在ViewGroup的onInterceptTouchEvent(MotionEvent ev)中判断ev的ACTION_DOWN(return false),ACTION_MOVE;ACTION_UP(return false理由同DOWN)属性,注意ACTION_DOWN不能返回true,否则剩下的两个事件将只能有自身消费不能传递给子View;在ACTION_MOVE中判断上下左右移动的位置,分别设置为true拦截,false不拦截给子类处理即可;

    /** * 拦截事件 * @param ev * @return */@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {    boolean intercepted = false;    int x = (int) ev.getX();    int y = (int) ev.getY();    switch (ev.getAction()) {        /*如果拦截了Down事件,则子类不会拿到这个事件序列,整个事件只能有本身处理*/        case MotionEvent.ACTION_DOWN:            lastXIntercept = x;            lastYIntercept = y;            intercepted = false;            if (!mScroller.isFinished()) {                mScroller.abortAnimation();                intercepted = true;            }            break;        case MotionEvent.ACTION_MOVE:            final int deltaX = x - lastXIntercept;            final int deltaY = y - lastYIntercept;            /*根据条件判断是否拦截该事件*/            if (Math.abs(deltaX) > Math.abs(deltaY)) {                intercepted = true;            } else {                intercepted = false;            }            break;        case MotionEvent.ACTION_UP:            intercepted = false;            break;    }    lastXIntercept = x;    lastYIntercept = y;    return intercepted;}

内部拦截法

  • 主要是requestDisallowInterceptTouchEvent(boolean),这个标志可以决定父控件是否拦截事件,如果设置了这个标志则不拦截,如果没设这个标志,它就会调用父控件的onInterceptTouchEvent()来询问父控件是否拦截。但这个标志对Down事件无效。

     if (actionMasked == MotionEvent.ACTION_DOWN) {            cancelAndClearTouchTargets(ev);            //清楚标志            resetTouchState();        }        // Check for interception.        final boolean intercepted;        if (actionMasked == MotionEvent.ACTION_DOWN                || mFirstTouchTarget != null) {                //标志            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;            }        } else {            // There are no touch targets and this action is not an initial down            // so this view group continues to intercept touches.            intercepted = true;        }
  • 重写子View的diapatchTouchEvent()

    public boolean dispatchTouchEvent(MotionEvent event) {...switch (action) {  case MotionEvent.ACTION_MOVE:      /**      跟上面的 !disallowIntercept  对应,当为true的时候表示父控件不拦截 false:拦截事件父类消费      */        getParent().requestDisallowInterceptTouchEvent(true);    break;  case MotionEvent.ACTION_MOVE:    if(子元素需要处理此事件)          getParent().requestDisallowInterceptTouchEvent(true);    break;  case MotionEvent.ACTION_UP: {    break;}...return super.dispatchTouchEvent(event);;}
  • 同时重写父类的onInterceptTouchEvent(),可以通过继承重写的方法

    @Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { /** 当为MotionEvent.ACTION_DOWN不拦截事件否则子类将不可能收到传递的事件 */  int action=ev.getAction();  if(action==MotionEvent.ACTION_DOWN){    return false;  }else {    return true;  }}
原创粉丝点击