Android中View事件分发机制

2018-10-18  本文已影响0人  邢皓翔

更多文章可以去我的CSDN

View事件分发机制

今天要写一写Android中比较重要的一个核心,View事件分发机制。那么事件分发机制是什么,为什么要写这个呢, 下面将一一讲解出来。

前言

相信大家对Android基础知识都已经有所了解啦,因为毕竟Android已经凉了,应该也没有多少新人再入坑了吧 (手动滑稽),所以本次直接写一下Android事件分发机制。
首先我们先说一下事件发起的源头MotionEvent

MotionEvent

在手指接触手机屏幕的那一刻就会开始进入MotionEvent的控制范围,所以我们先了解一下MotionEvent最常用的几个事件。

正常情况下,会触发如下几种情况:

下面可以引入事件分发机制原理了。

事件分发过程分析

简单来说,事件分发过程就是从手指接触屏幕到弹起所有的事件流通的流程。我们现在一步一步的分析这个流程。

Activity

为什么先说Activity呢? 因为点击事件MotionEvent最先传递到的就是Activity,所以先了解一下Activity构成是很重要的。当我们写Activity时总会调用setContentView来设置布局,所以先看看setContentView方法是怎么实现的。

代码如下

// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

这里调用了getWindow().setContentView(layoutResID); g我们继续看getWindow到底get到了什么。

public Window getWindow() {
    return mWindow;
}

这里返回了mWindow,继续翻阅代码后发现在Activity的attach方法中,初始化了mWindow。

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);
        // 省略后面代码
    }

getWindow指的是PhoneWindow对象,所以继续看PhoneWindow对象的setContentView

@Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            view.setLayoutParams(params);
            final Scene newScene = new Scene(mContentParent, view);
            transitionTo(newScene);
        } else {
            mContentParent.addView(view, params);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

我们来看一下installDecor();这个方法实现了什么。

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
        }
}

这里没发现什么继续看的generateDecor方法

protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

这里创建出了DecorView,这个DecorView就是Activity的根View,整体的View体系结构入下图所示

a.png

我们平时添加的布局就是现实在ContentView中。

事件传递的方法

了解Activity之后能够很好的帮助我们了解整个View的体系,从而也能深刻了解事件分发机制。

我们需要先了解三个很重要的方法。

onTouchEvent(MotionEvent ev) 用来处理点击事件。
上面三个方法的具体使用,可以参考下面伪代码实现

public boolean dispatchTouchEvent(Motion ev){
    boolean consume = true;
    if (onInterceptTouchEvent(ev)){
        consume = onTouchEvent(ev);
    } else {
        consume = child.dispatchTouchEvent(ev);
    }
    return comsume;
}

上述伪代码应该大家能看清他们之间的联系了吧,我用一张图来讲解事件
![B__)4}C~9}IHV6[CN}W]J0.png

当点击View的时候事件顺序是这样的 ViewGroupA -> ViewGroupB -> View,事件传递的时候先执行dispatchTouchEvent方法,再执行onInterceptTouchEvent方法。

事件处理的顺序是View-> ViewGroupB -> ViewGroupA 事件处理都是执行的onTouchEvent方法
事件传递的返回值很简单,true拦截, false不拦截
事件处理方法也一样,true 处理, false不处理给上级。
我们可以把这个传递过程拿到生活中来对应,比如说View是底层干活的员工,ViewGroupB是我们的主管领导,ViewGroupA是我们的总监,现在老板安排下来一件事,肯定是总监安排给主管领导,领导再交给我们来做。

事件处理过程基本相似,我们做完任务后给上级领导汇报,传入false,然后上级领导给总监汇报,总监最终去给老板汇报。

大体流程图是这样的

![FWB(UE]XZES1VSNS{1T9}I.png

说到这里应该基本上都知道View事件传递过程了吧。其实做开发不能死背概念,应该把概念换成生活中的一些事情,这样理解起来容易多了。 (至少我是这样)。

参考

上一篇 下一篇

猜你喜欢

热点阅读