android事件分发

参考文章
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();
猜测这里是开始绘制的地方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值