参考文章
https://www.jianshu.com/p/d437a524c081
https://www.jianshu.com/p/d437a524c081
http://blog.csdn.net/feiduclear_up/article/details/47356429
https://www.2cto.com/kf/201606/520727.html
dispatchTouchEvent
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)把事件传递给了ViewGroup。
View、ViewGroup是否消费事件,如果消费事件,那么直接返回true;如果不消费,那么走Activity的onTouchEvent决定是否消费事件。
2.ViewGroup
return super.dispatchEvent(); 走onInterceptTouchEvent,如果return true代表父控件拦截掉事件,直接走ViewGroup的onTouchEvent;return false/super.onInterceptTouchEvent代表父控件不拦截,传递到View
先看View是否消费事件,如果不消费事件,那么由自己的onTouchEvent决定是否消费事件。
3.View
由自己的onTouchEvent决定是否消费事件。
onTouchEvent
View,ViewGroup,Activity return true代表消费事件,否则代表不消费事件。
单独针对View来说
View.dispatchToucheEvent
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
可以看到如果View设置了onTouchListener,状态是enabled,那么是否走onTouchEvent(event)就完全取决于mOnTouchListener.onTouch(this, event)的返回值。如果返回true,那么就不走onTouchEvent,如果返回false,那么就走onTouchEvent,也就是说onTouchListener.onTouch方法的优先级高于onTouchEvent。
对于onTouchEvent的返回值,取决于View是否可用以及是否有点击事件。
在onTouchEvent方法中。
1.当控件不可用时:
当控件有点击事件,返回true,但不会调用onClick等点击事件
当空间无点击事件,返回false
2.当控件可用时:
无点击事件,返回false
有点击事件,就走Switch()判断,判断是move,down,up,cancel,最后返回true
在up时,会调用preformClick()------>onClick()事件。
由于onClick方法是在onTouchEvent的ACTION_UP手势里面执行的,也就是dispatchTouchEvent->onTouch->onTouchEvent->onClick
具体的分析可查看开头参考的文章。
补充1:
http://blog.csdn.net/yanbober/article/details/45912661
http://blog.csdn.net/yanbober/article/details/45887547
http://blog.csdn.net/yanbober/article/details/45932123
dispatchTouchEvent事件派发是传递的,如果返回值为false将停止下次事件派发,如果返回true将继续下次派发。譬如,当前派发down事件,如果返回true则继续派发up,如果返回false派发完down就停止了。
补充2:
Android中MotionEvent的来源和ViewRootImpl
http://blog.csdn.net/singwhatiwanna/article/details/50775201
硬件捕获到点击事件
->WMS
->ViewRootImpl
->DecorView.dispatchPointerEvent
->cb.dispatchTouchEvent也就是Activity.dispatchTouchEvent
->PhoneWindow.superDispatchTouchEvent
->DecorView.superDispatchTouchEvent
补充3:
Android应用setContentView与LayoutInflater加载解析机制源码分析
http://blog.csdn.net/yanbober/article/details/45970721
当启动Activity调运完ActivityThread的main方法之后,接着调用ActivityThread类performLaunchActivity来创建要启动的Activity组件,在创建Activity组件的过程中,还会为该Activity组件创建窗口对象和视图对象;接着Activity组件创建完成之后,通过调用ActivityThread类的handleResumeActivity将它激活。
在handleResumeActivity中调用了r.activity.makeVisible();
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
WindowManager的实现类是WindowManagerImpl,最终调用了WindowManagerGlobal.addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
...这里省略了一堆代码
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
addView过程中会创建ViewRootImpl,然后调用ViewRootImpl的setView方法。在ViewRootImpl的setView方法(此方法运行在UI线程)中,会通过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上,在这个过程中,ViewRootImpl、DecorView和WMS会彼此关联,同时会创建InputChannel、InputQueue和WindowInputEventReceiver来接受点击事件的消息。
也就是:
attach->创建PhoneWindow(mWindow),PhoneWindow与Activity绑定。
onCreate->setContentView创建DecorView(mDecor),contentParent,并将布局添加到mDecor
handleResumeActivity->在onResume调用后创建了ViewRootImpl(root),将DecorView(mDecor)最终添加到Window上,设置mDecor可见,而且开始接受点击事件。
从应用第一个Activity被创建
ActivityThread main
->attachApplication
->sheduleLaunchActivity
->handleLaunchActivity
->performLaunchActivity
->Activity.attach
新建了一个PhoneWindow实例mWindow,通过 mWindow.setCallbackPhoneWindow
与Activity绑定。
->Instrumentation.callActivityOnCreate
->Activity.performCreate
->onCreate
->setContentView(int resId)
->PhoneWindow.setContentView
新建了一个DecorView实例mDecor,把布局resId添加到DecorView,然后找到
andoird.R.id.content作为根布局contentParent。
->ActivityThread.handleResumeActivity
->performResumeActivity
->r.activity.performResume();
->Instrumentation.callActivityOnResume
-> activity.onResume();
->r.activity.makeVisible();
->Activity.makeVisible()
wm.addView(mDecor, getWindow().getAttributes());
mDecor.setVisibility(View.VISIBLE);
->WindowManagerGlobal.addView
创建了一个ViewRootImpl对象root,
root = new ViewRootImpl(view.getContext(), display);
//将DecorView最终添加到Window上
root.setView(view, wparams, panelParentView);
另外setView方法中
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
猜测这里是开始绘制的地方