在触摸屏幕的过程中,要涉及到和控件的交互,如何处理多个控件之间的事件处理,保证正常的交互效果。我们今天来看事件分发机制。

零、事件分发的一些基础知识

什么是事件?

当用户触摸屏幕时,发生的点击或者触摸动作,称之为点击事件(Touch事件)。Touch事件的一些详细内容,比如触摸的位置,触摸的类型,时间的长短等被封装在一个叫做MotionEvent对象中。

事件类型

  • MotionEvent.ACTION_DOWN:按下View,所有触摸或者点击事件的开始
  • MotionEvent.ACTION_UP:抬起View,触摸或者点击事件的结束,与Down对应相反
  • MotionEvent.ACTION_MOVE:触摸动作在屏幕上进行滑动
  • MotionEvent.ACTION_CANCEL:取消事件,结束事件

因为在屏幕上,是分层的,比如是现有窗口,然后有交互界面(Activity),界面中有布局(ViewGroup),布局中有控件(View)。

那么如何确定到底该哪个控件来响应处理触摸事件呢,这就要用到事件分发机制了。一般来说,事件传递的顺序是:Activity -> ViewGroup -> View。

一、事件分发

事件的分发顺序

1个点击事件发生后,事件先传到Activity、再传到ViewGroup、最终再传到 View。

事件分发的过程

在整个事件分发,并响应事件的过程中,有三个重要的方法:

  • dispatchTouchEvent:分发(传递)点击事件,当点击事件能够传递给当前View时,该方法就会被调用。
  • onInterceptTouchEvent:判断是否拦截某个事件,该方法仅在ViewGroup中存在。一般情况下会在ViewGroup的dispatchTouchEvent方法中调用该方法。
  • onTouchEvent:处理点击事件,在dispatchTouchEvent内部调用。

二、Activity 中的事件分发机制

Activity 中包含两个事件分发与处理的方法,分别是:

  • boolean dispatchTouchEvent(MotionEvent ev):事件分发
  • boolean onTouchEvent(MotionEvent event):事件消费
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction();
    }
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}

第一步是判断事件如果是点击事件,则调用 onUserInteraction 方法,该方法的方法体为空,可以重写这个方法,用于处理一些交互问题。

第二步是调用了 Window 的 superDispatchTouchEvent 方法,如果返回 true,则表示此事件已被消费,结束此次分发流程,false 则继续调用该 Activity 的 onTouchEvent 方法处理该事件。

总结一下,事件进入 Activity的dispatchTouchEvent 开始分发,首先会将该事件传递给该页面的 ViewGroup,ViewGroup 如果消费了该事件,则分发结束,未消费则继续调用 Activity#onTouchEvent 方法处理事件,流程如图:

 

三、ViewGroup 中的事件分发

ViewGroup 中包含三个事件分发相关的方法:

  • dispatchTouchEvent(MotionEvent ev):
  • onIntercepTouchEvent(MotionEvent ev):
  • onTouchEvent(MotionEvent ev):

1、事件首先会进入 dispatchTouchEvent 方法开始分发。

2、dispatchTouchEvent方法通过调用onInterceptTouchEvent 方法判断是否需要拦截事件。

  • 2.1、如果需要拦截(方法返回 true),则表示当前 ViewGroup 希望处理该事件,或者不希望子 View 处理该事件,此时将直接调用 onTouchEvent 方法。
  • 2.2、如果不需要拦截,则将该事件传递给子 View 的 onTouchEvent 方法,或者子 ViewGroup 的 dispatchTouchEvent 方法。

3、onTouchEvent 方法, 该方法用于消费事件,返回值表示是否已消费,在两种情况下会被调用:onInterceptTouchEvent 方法确认需要拦截该事件以及子 View/ViewGroup 未消费该事件。

  • 3.1、如果该方法返回 true,则表示事件已消费,此次事件分发就成结束;
  • 3.2、如果返回 false,则表示未消费该事件,会继续调用父 ViewGroup 或 Activity 的 onTouchEvent 方法。

ViewGroup的事件分发机制如下:

 

 

四、View 中的事件分发机制

View 中包含如下两个用于处理事件分发相关的方法:

  • dispatchTouchEvent(MotionEvent event)
  • onTouchEvent(MotionEvent event)

ViewGroup 会将事件传递给 dispatchTouchEvent 方法,由于不是 ViewGroup,不需要考虑向下传递事件的逻辑,所以 View 中的事件处理流程很简单。

 

 

五、事件分发总结