Android view的事件分发机制

Android 自定义view

Android自定义view有如下步骤:

  • 创建view(View子类)
  • 处理view的布局
  • 绘制view
  • 与用户进行交互

android view视图层次

视图层次

view的事件分发机制(与用户进行交互):

MotionEvent

事件分发主要传递的其实就是一系列MotionEvent,看一下他的主要方法:

  • getX()/getY()获取该触摸点相对于当前view的左上角坐标

  • getRawX()/getRawY()获取触摸点相对于整个手机屏幕的坐标

  • getAction()获取事件类型

  • 这里注意一下,其实,Android里有两套坐标系:

    • Android坐标系:以屏幕左上角为顶点getRawY(),getRawX()

    • view坐标系 :以view左上角为起点,getX(),getY()其实他是一个概念,更好的帮助我们开发
      坐标系

事件分发就是将一个MotionEvent传递给一个具体的view。
事件传递的主要过程:
activity—>
window—->
viewgroup(view)

事件分发三大方法:

boolean dispatchTouchEvent(MotionEvent ev)

  • 事件分发的主要方法

  • 如果事件能够传递给当前view,那么他的dispatchTouchEvent方法一定会被调用

  • 伪代码描述:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public boolean onDispatchTouchEvent(MotionEvent ev){
    boolean result=false;
    if(onInterceptTouchEvent(ev)){
    //也就是调用了view的方法,viewgroup继承自view
    result=onTouchEvent;
    }else{
    result=child.dispatchTouchEvent(ev)

    }
    return result;
    }

    优先级顺序

(view)的dispatchTouchEvent—>
onTouch()—->
onTouchEvent()—–>
onclick()

boolean onInterceptTouchEvent(MotionEvent ev)
表示是否拦截此次事件 ,默认返回false
boolean onTouchEvent(MotionEvent ev)
处理触摸事件的主要方法,(如果设置有onTouchListener设置了onTouch,则会调用onTouch,且返回true则不会调用onTouchEvent),里面调用了onClick,onLongClick,(且只要LONG_CLICKABLE或者CLICKABLE这两个任意一个为true,就会返回true),即便是不可用状态

dispatchTouchEvent主要流程(一次viewGroup到view的传递过程,其他的也是这样,递归着来):

  1. (viewGroup)判断是否拦截,如果拦截 (onterceptTouchEvent返回true),则接下来的事件序列都有其来处理(调用他的onTouchEvent),否则进行下一步
  2. 遍历viewGroup里所有的子view(viewGroup),找到符合的view(在点击坐标是否在view内,且view是否在播放动画),如果找到,则调用他的dispatch方法,即完成一次传递(通过dispatchTransformedTouchEvent()
  3. 如果遍历所有子元素后事件都没有被处理,则包含两种情况:
    • viewgroup没有合适子元素
    • 子元素处理了点击事件,但dispatchTouchEvent返回了false(一般是onTouchEvent返回了false)

则继续交由该viewgroup处理

总结一下:

  1. 一但viewGroup拦截了这个事件,则这一整个序列都将交由他来处理,并且不会再次调用onInterceptTouchEvent()方法。所以如果viewGroup想要处理事件,就一是拦截了事件,二是没有找到合适的子view,从而转去调用它自己的onTouchEvent。
  2. view一旦开始处理事件,如果他不消耗ACTION_DOWN,则其他事件也不会交由他处理,
    view如果不消耗除了ACTION_DOWN以外的其他事件,则这事件会消失,最后将传到activity处理
  3. 注意onclick的调用时机是view接收到了ACTION_DOWN和ACTION_UP这两个事件。
  4. onInterceptTouchEvent的调用:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // ViewGroup

    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;
    }
  • 也就是说如果被拦截,那么mfirstTouchTarget就为null,则跳出去,就不会再次执行onInterceptTouchEvent,所以一定注意这个方法只会执行一次。
  1. disallowIntercept 而且从上面一段源码也可看到有一个特殊的标记量disallowIntercept,这个变量可以由requestDisallowInterceptTouchEvent()设置,他可以不允许viewgroup拦截事件,所以,如果disallowIntercept==true,就直接跳过,不执行 onInterceptTouchEvent(ev),他的作用是可以由子view去调用getParent().requestDisallowInterceptTouchEvent(true);从而处理滑动冲突

  2. 总的来说,view的事件传递就是利用了责任链设计模式,从activity开始忘viewGroup,再往下级view传递,(从DOWN开始,down事件确定了该view是否要消费他)如果能执行,能消费,则该view返回true,表示交由他来处理,并且停止向下传递;而如果返回了false则交由上级view来处理,而上级viewgroup可以拦截下事件,从而将接下来的事件交由他来处理。

© 2020 WPY's Android Tour All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero