Android 事件分发

不详细介绍事件分发的流程和逻辑。

这里关注一下activity view对于touch/key事件接受处理的先后顺序。


既然是activity和view的事件接受顺序,那么只考虑dispatchTouchEvent和onTouchEvent;

直接看结果,然后再看why(你总的先看一眼妹子好不好看,然后在去提亲不是)

结果是:

1.dispatchTouchEvent:activity先响应,view后响应;

2.onTouchEvent:activity后响应,view先响应;

注意关键字‘先后’。


再看看why?

简单的,demo打堆栈,截取部分:

at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2657)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:444)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1829)
        at android.app.Activity.dispatchTouchEvent(Activity.java:3394)
        at com.shixin398.myapplication.MainActivity.dispatchTouchEvent(MainActivity.java:31)
        at android.support.v7.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:68)
        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:402)
        at android.view.View.dispatchPointerEvent(View.java:12567)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5031)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4834)

viewroot中processPointerEvent可能大部分人很陌生,没关系,不需要care;知道这个是进行touch事件派发的最早期入口就ok。

(搞事情,不要妄图一下啥都搞明白,慢慢来)

随后走到view中的dispatchPointerEvent,这个view实际是decorview,会走到decorview的dispatchTouchEvent。这个函数就比较熟悉了,经常需要复写的一个函数。

12062    public final boolean dispatchPointerEvent(MotionEvent event) {
12063        if (sDebugDispatchInput) {
12064            Log.d(VIEW_LOG_TAG, "dispatchPointerEvent event is TouchEvent ? "
12065                    + event.isTouchEvent() + " ,event: " + event);
12066        }
12067        if (event.isTouchEvent()) {
12068            return dispatchTouchEvent(event);//当前view是decorview,多态么,就走decorview的方法
12069        } else {
12070            return dispatchGenericMotionEvent(event);
12071        }
12072    }

到decoreview就看下源码:

394    @Override
395    public boolean dispatchTouchEvent(MotionEvent ev) {
396        final Window.Callback cb = mWindow.getCallback();
397        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
398                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
399    }

一、dispatchTouchEvent:activity先响应,view后响应

window.callback,关于activity和窗口的关系,现在也不需要详细了解,知道这个callback对应当前的activity就ok。

那么就走到activity的dispatchTouchEvent了。

所以1点的逻辑就是刚才讲述的逻辑了:1.dispatchTouchEvent:activity先响应,view后响应;

如果activity拦截了事件,则decorview中的view不会收到touch事件。若不拦截,则会继续派发。

这个继续派发,也会看到2的逻辑分析,如下。


二、onTouchEvent:activity后响应,view先响应

如果activity没有拦截,那么会派发的viewgroup。看下acitivity code便知:

3396    public boolean dispatchTouchEvent(MotionEvent ev) {
3397        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3398            onUserInteraction();
3399        }
3400        if (getWindow().superDispatchTouchEvent(ev)) {//通过getwindow来把事件给窗口,window在给decorview,decorview就给activity里的控件了(viewgroup/view)
3401            return true;
3402        }
3403        return onTouchEvent(ev);//最后是onTouchEvent,论证了2点
3404    }
代码中:
if (getWindow().superDispatchTouchEvent(ev))

getwindow拿到的是phonewidnow,代码如下:

1827    @Override
1828    public boolean superDispatchTouchEvent(MotionEvent event) {
1829        return mDecor.superDispatchTouchEvent(event);
1830    }

然后是decorview的,又看到熟悉的dispatchTouchEvent了:

474    public boolean superDispatchTouchEvent(MotionEvent event) {
475        return super.dispatchTouchEvent(event);
476    }
public class DecorView extends FrameLayout
public class FrameLayout extends ViewGroup 
调用super的,而super是framelayout,里面没有复写dispatchTouchEvent,自然也就调用viewgroup中的dispatch了。


到viewgroup里就不在详细讲述了,这个事件链很麻烦,真心不想写。

知道viewgroup这里dispatchtouchevent 经过层层遍历递归,会最终调用到具体的view的OnTouchEvent就ok了。(可以看下任玉刚的书中写的这部分,或者csdn一下也ok)。


到此,知道通过activity的下面逻辑,走到viewgroup的dispatchTouchevent,随后递归派发touch事件。如果没有没有view响应,也就是return false了。

getWindow().superDispatchTouchEvent(ev)

才会执行activity的onTouchEvent。所以dispatchTouchEvent:activity先响应,view后响应;onTouchEvent:activity后响应,view先响应;

关键函数:activity的dispatchTouchEvent,先if最后return onTouchEvent。

3396    public boolean dispatchTouchEvent(MotionEvent ev) {
3397        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
3398            onUserInteraction();
3399        }
3400        if (getWindow().superDispatchTouchEvent(ev)) {//通过getwindow来把事件给窗口,window在给decorview,decorview就给activity里的控件了(viewgroup/view)
3401            return true;
3402        }
3403        return onTouchEvent(ev);//最后是onTouchEvent,论证了2点
3404    }


至此,结束。

只分析了dispatch 和onTouchEvent 在acitivity和view之间,调用的先后顺序,以便在进行事件处理时,可以合理的在自定义view和acitivity之间进行事件的拦截等操作。




猜你喜欢

转载自blog.csdn.net/shi_xin/article/details/80603703
今日推荐