前言
首先,Android系统为什么会定义一套事件分发机制?
其实很简单,因为我们的布局里面View,ViewGroup是树状结构,一组事件顺序流过来,到底是View处理还是ViewGroup处理,都是在Android 事件分发机制下运行的,有了Android事件分发机制我们可以有效避免事件冲突,或者是可以很方便的自定义触摸反馈。
Android事件分发机制
机制下的核心类
public final class MotionEvent extends InputEvent implements Parcelable {
/**
* Constant for {@link #getActionMasked}: A pressed gesture has started, the
* motion contains the initial starting location.
* <p>
* This is also a good time to check the button state to distinguish
* secondary and tertiary button clicks and handle them appropriately.
* Use {@link #getButtonState} to retrieve the button state.
* </p>
*/
public static final int ACTION_DOWN = 0;
/**
* Constant for {@link #getActionMasked}: A pressed gesture has finished, the
* motion contains the final release location as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_UP = 1;
/**
* Constant for {@link #getActionMasked}: A change has happened during a
* press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}).
* The motion contains the most recent point, as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_MOVE = 2;
/**
* Constant for {@link #getActionMasked}: The current gesture has been aborted.
* You will not receive any more points in it. You should treat this as
* an up event, but not perform any action that you normally would.
*/
public static final int ACTION_CANCEL = 3;
//省略非核心代码
......
}
机制下的核心方法
- dispatchTouchEvent()
- onInterceptTouchEvent()
- onTouchEvent()
机制的关键知识点
- Activity最顶层的Window是PhoneWindow,PhoneWindow最顶层的View是DecorView,DecorView是继承FrameLayout,DecorView下面是一个LinearLayout,
LinearLayout下面有两个子View,两个子View都是继承FrameLayout。 - 一个clickable或者longClickable的View会永远消费Touch事件。
- 长按事件是在ACTION_DOWN事件中执行的,要想执行长按事件该View必须设置longClickable,然后通过OnLongClickListener回调给上层应用处理,期间不能产生ACTION_MOVE事件。
- 点击事件是在ACTION_UP事件中执行的,前提是消费了ACTION_DOWN事件,并且没有设置OnLongClickListener的情况下,如设置了OnLongClickListener的情况,则必须使onLongClick()返回false。
- 如果View设置了onTouchListener了,并且onTouch()方法返回true,则不执行View的onTouchEvent()方法,也表示View消费了Touch事件,返回false则继续执行onTouchEvent()。
- 事件流分发过程中,子View如果处理了ACTION_DOWN事件,但是后面的事件流里面的事件被父View,在onInterceptTouchEvent中被拦截,那么父View 就接管后面的事件流,并且向处理了ACTION_DOWN的子View发送一个ACTION_CANCEL事件。
触摸反馈
- 对于触摸反馈,一般是我们在自定义View的时候,在Android事件分发机制下,来使用,比如我们需要在自定义的View里面做自己的触发算法,这个时候需要重写View的onTouchEvent方法,在里面写上你的触摸反馈算法,并返回 true(关键是 ACTION_DOWN 事件时返回 true。
- 如果是会发生触摸冲突的 ViewGroup,还需要重写 onInterceptTouchEvent(),在事件流开始时返回 false,并在确认接管事件流时返回一次 true,以实现对事件的拦截。
- 当子 View 临时需要父View 拦截事件流时,可以调用父 View 的 requestDisallowInterceptTouchEvent() ,通知父 View 在当前事件流中不再尝试通过 onInterceptTouchEvent() 来拦截。