PS:很多知识看了忘,忘了看。但是却缺少梳理。索性自个简单梳理下自己看过的东西。
1、Activity
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction(); } if (getWindow().superDispatchTouchEvent(ev)) { return true; } return onTouchEvent(ev); }
getWindow().superDispatchTouchEvent(ev) 这里的getWindow 其实就是 PhoneWindow示例。
2、PhoneWindow
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }Activity 其实就是调用这个方法。而PhoneWindow这个方法却是去调用DecorView的 superDispatchTouchEvent(ev)
3、DecorView
PS:看源码发现 23 DecorView还是PhoneWindow的私有内部了,27 却已经不在是内部类了,独立一个文件的pulice类了。
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }
@Override public boolean dispatchTouchEvent(MotionEvent ev) { final Window.Callback cb = mWindow.getCallback(); return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); }
PS:其实DecorView 继承了FramLayout ,已经是ViewGroup了,调用的super.dispatchTouchEvent(ev),已经交个ViewGroup了。
cb 是什么呢?请看 浅析 Activity 中为何会有 onTouchEvent?
Window 定义了一个 Callback 接口,列出了一系列需要由 Window 的代理处理的事件,当 Activity 一被创建时,
Activity 就是 Window 的 Callback: mWindow.setCallback(this);
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window, ActivityConfigCallback activityConfigCallback) { attachBaseContext(context); mFragments.attachHost(null /*parent*/); mWindow = new PhoneWindow(this, window, activityConfigCallback); mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this);
4、ViewGroup
@Override public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); }
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled;PS:奇怪,找了 16 17 21 23 和 27 各大神博客写的如下代码段:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean result = false; // 默认状态为没有消费过 if (!onInterceptTouchEvent(ev)) { // 如果没有拦截交给子View result = child.dispatchTouchEvent(ev); } if (!result) { // 如果事件没有被消费,询问自身onTouchEvent result = onTouchEvent(ev); } return result; }
5、在View 内 onTouchListener 先雨 onTouchEvent调用。并且如果 onTouchListener 返回true 在不在调用 onTouchEvent,
同时也无法执行onClickListener和onLongClickListener事件。
因为 onClickListener 和 onLongClickListener 都在OnTouchEvent 事件里面调用。
if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo li = mListenerInfo; if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) { result = true; } if (!result && onTouchEvent(event)) { result = true; } }
看以看到 li.mOnTouchListener.onTouch(this, event 返回true。 result=true。就无法执行到onTouchEvent(event);
6、onLongClickListener 在 onTouchEvent 的 ACTION_DOWN 里面调用。而 onClickLisener在 onTouchEvent 的 ACTION_UP 里面调用。
所以如果是长按,则先执行 onLongClickListener 然后在 执行 onClickListener.(代码太长就不贴了,可自行查看view源码)
核心要点 来自:点击打开链接
- 事件分发原理: 责任链模式,事件层层传递,直到被消费。
- View 的
dispatchTouchEvent
主要用于调度自身的监听器和 onTouchEvent。 - View的事件的调度顺序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。
- 不论 View 自身是否注册点击事件,只要 View 是可点击的就会消费事件。
- 事件是否被消费由返回值决定,true 表示消费,false 表示不消费,与是否使用了事件无关。
- ViewGroup 中可能有多个 ChildView 时,将事件分配给包含点击位置的 ChildView。
- ViewGroup 和 ChildView 同时注册了事件监听器(onClick等),由 ChildView 消费。
- 一次触摸流程中产生事件应被同一 View 消费,全部接收或者全部拒绝。
- 只要接受 ACTION_DOWN 就意味着接受所有的事件,拒绝 ACTION_DOWN 则不会收到后续内容。
- 如果当前正在处理的事件被上层 View 拦截,会收到一个 ACTION_CANCEL,后续事件不会再传递过来。