Android开发Android开发经验谈Android技术知识

View的事件分发机制和滑动冲突解决总结

2018-06-08  本文已影响28人  Joe_blake

Touch事件处理和传递

  1. 事件分类:

    ​ ACTION_DOWN, ACTION_UP, ACTION_MOVE:按下、离开、移动

    ​ ACTION_POINTER_DOWN, ACTION_POINTER_UP:多点按下、离开

    ​ ACTION_CANCEL:当控件收到前驱事件(前驱事件:一个从DOWN一直到UP的所有事件组合称为完整的手势,中间的任意一次事件对于下一个事件而言就是它的前驱事件)之后,后面的事件如果被父控件拦截,那么当前控件就会收到一个CANCEL事件,并且把这个事件会传递给它的子View,没有子View就调用父容器的dispatchTouchEvent()。

            if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
                event.setAction(MotionEvent.ACTION_CANCEL);
                if (child == null) {
                    handled = super.dispatchTouchEvent(event);
                } else {
                    handled = child.dispatchTouchEvent(event);
                }
                event.setAction(oldAction);
                return handled;
            }
    

    ViewGroup的dispatchTransformedTouchEvent方法对ACTION_CANCEL的处理

  2. 事件处理方法:

    • 传递—-dispatchTouchEvent():

      只要事件传递到了当前View,那么dispatchTouchEcent方法就一定会被调用。返回结果表示是否消耗当前事件。

    • 拦截——onInterceptTouchEvent():

      在dispatchTouchEcent方法内部调用此方法,用来判断是否拦截某个事件。如果当前View拦截了某个事件,那 么在这同一个事件序列中,此方法不会再次被调用。返回结果表示是否拦截当前事件。

    • 消费—-onTouchEvent()函数和OnTouchListener():

      在dispatchTouchEcent方法内调用此方法,用来处理事件。返回结果表示是否处理当前事件,如果不处理,那么在同一个事件序列里面,当前View无法再收到后续的事件。

  3. Android事件传递结论:

    1. 事件都是从Activity.dispatchTouchEvent()开始传递
    2. 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递
    3. 某个View一旦决定拦截事件,那么这个事件序列之后的事件都会由它来处理,并且不会再调用onInterceptTouchEvent。
    4. 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来
    5. 如果一个View处理了down事件,却没有处理其他事件,那么这些事件不会交给父元素处理,并且这个View还能继续受到后续的事件。而这些未处理的事件,最终会交给Activity来处理。
    6. ViewGroup的onInterceptToucheEvent默认返回false,也就是默认不拦截事件。
    7. View没有InterceptTouchEvent方法,如果有事件传过来,就会直接调用onTouchEvent方法。
    8. View的enable属性不会影响onTouchEvent的默认返回值。就算一个View是不可见的,只要他是可点击的(clickable或者longClickable有一个为true),它的onTouchEvent默认返回值也是true。
    9. 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
    10. OnTouchListener优先于onTouchEvent()对事件进行消费
  4. View拦截处理事件的流程,伪代码表示:

public boolean dispatchTouchEvent(MotionEvent ev){     
    boolean consum = false;     
    if(onInterceptTouchEvent(ev)){      //判断是否拦截        
        consum = onTouchEvent(ev);      //处理事件 
    }else{                              //不拦截,则交给子View的dispatchTouchEvent()方法处理,
        consum = child.dispatchTouchEvent(ev);     
    }          
    return consum; 
}

滑动冲突解决

外部拦截法:

public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean intercepted = false;
    int x = (int)event.getX();
    int y = (int)event.getY();
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            intercepted = false;
           break;
       }
       case MotionEvent.ACTION_MOVE: {
           if (满足父容器的拦截要求) {
                intercepted = true;
           } else {
                intercepted = false;
           }
           break;
       }
       case MotionEvent.ACTION_UP: {
           intercepted = false;
           break;
       }
       default:
           break;
       }
       return intercepted;
}

内部处理法:

//重写父容器的onInterceptTouchEvent
public boolean onInterceptTouchEvent(MotionEvent event) {

     int action = event.getAction();
     if (action == MotionEvent.ACTION_DOWN) {//不处理DOWN事件,全部交给子View判断
         return false;
     } else {
         return true;
     }
 }
//重写子View的dispatchTouchEvent方法
public boolean dispatchTouchEvent(MotionEvent event) {
     int x = (int) event.getX();
     int y = (int) event.getY();

     switch (event.getAction()) {
     case MotionEvent.ACTION_DOWN: {
        //父Viewgroup不处理onInterceptTouchEvent回调
         parent.requestDisallowInterceptTouchEvent(true);
         break;
     }
     case MotionEvent.ACTION_MOVE: {
         int deltaX = x - mLastX;
         int deltaY = y - mLastY;
         if (父容器需要此类点击事件) {
            //需要父Viewgroup处理onInterceptTouchEvent回调,拦截事件
             parent.requestDisallowInterceptTouchEvent(false);
         }
         break;
     }
     case MotionEvent.ACTION_UP: {
         break;
     }
     default:
         break;
     }

     mLastX = x;
     mLastY = y;
     return super.dispatchTouchEvent(event);
 }
上一篇下一篇

猜你喜欢

热点阅读