Android view 的事件分发机制
1 事情的传送次序是 Activity -> Window -> 高层View
touch 事情造成后,最开始由 activity 的 dispatchTouchEvent 解决
/** * Called to process touch screen events. You can override this to * intercept all touch screen events before they are dispatched to the * window. Be sure to call this implementation for touch screen events * that should be handled normally. * * @param ev The touch screen event. * * @return boolean Return true if this event was consumed. */ public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
然后事情会传入 Window 的 superDispatchTouchEvent。 假如全部的 view 也没有消費事情,最终会交到 activity 的 onTouchEvent 解决。
2 Window 是一个抽象类,它的唯一完成类是 PhoneWindow
/** * Abstract base class for a top-level window look and behavior policy. An * instance of this class should be used as the top-level view added to the * window manager. It provides standard UI policies such as a background, title * area, default key processing, etc. * * <p>The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window {
}
查询 PhoneWindow 的 superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
启用了 mDecor 的 superDispatchTouchEvent
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {} // This is the top-level view of the window, containing the window decor. private DecorView mDecor;
mDecor 便是 DecorView, DecorView 是一个 FrameLayout, 便是大家 setContentView 所设定 view 的父器皿。
继续看 DecorView 的superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }
启用了父类的 dispatchTouchEvent, FrameLayout 中沒有 dispatchTouchEvent, 事实上启用了 FrameLayout 的父类 ViewGroup 的 dispatchTouchEvent。
到此事情就传送到高层View(ViewGroup) 中,然后事情就从高层 view 逐渐向子view派发。
3 事情派发关键涉及到 3 个方式
public boolean dispatchTouchEvent(MotionEvent event)
事情传送到一个 view 时便会先启用这一 view 的 dispatchTouchEvent 开展向下派发
public boolean onInterceptTouchEvent(MotionEvent ev)
开展事情的阻拦,仅有 ViewGroup 有阻拦方式, 单一View沒有,事情传入 单一View 就立即启用 onTouchEvent 开展解决了
public boolean onTouchEvent(MotionEvent event)
解决点击事件,true 表明消費这一事情, false 表明不消費
4 下边的伪代码能够非常好的表明事情的派发全过程
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false; if (onInterceptTouchEvent(ev)) { consume = onTouchEvent(ev); }else { consume = child.dispatchTouchEvent(ev); } return consume; }
事情传送到高层 ViewGroup 之后, 便会启用 ViewGroup dispatchTouchEvent 开展派发。假如这一 ViewGroup 的 onInterceptTouchEvent 回到 True 表有它要阻拦这一事情,然后便会启用它的 onTouchEvent 开展解决。如果不阻拦则会交到它的子 view 再次开展派发, 这般不断直至事情被最后解决。
一切正常状况下,一个事情编码序列只有被一个 view 阻拦且耗费。一个 view 一旦阻拦了某一事情,那麼同一个事情编码序列内的全部的事情都是会交到它解决。
假如一个 view 的 onTouchEvent 回到 false, 那麼它的父器皿的 onTouchEvent 可能被启用,依次类推。假如全部的 view 也不解决这一事情,这一事情最终会回到到 activity 的 onTouchEvent 开展解决。
事情派发实际关键点比较繁杂,但基础步骤便是上边的伪代码。事情派发体制是解决滚动矛盾的压根,知道基本原理,碰到难题再多看一下源代码就可以了。