androidEvent

Introduction: 对 android 事件分发机制的一点个人理解,将事件机制进行了简单化处理,刚方便理解!
More: Author   ReportBugs   
Tags:
事件处理-View事件分发-ViewGroup事件分发-

android 中的事件处理一直以来困扰不少刚刚从事 android 开发的同学,网上也有不少讲解 android 事件分发的文章,然而讲解的都不够简洁!现在我将用另一种简洁的方式来讲解 android 事件的分发机制!

android 的事件分发可以简单的归位两类:1.view 的事件分发和处理 2.ViewGroup 的事件分发和处理。我们首先分析 View 的事件分发和处理。

View 的事件分发和处理

View 中的事件分发和处理涉及到了两个方法: 1.public boolean dispatchTouchEvent(MotionEvent event) 2.public boolean onTouchEvent(MotionEvent event) 它们的返回值都为 boolean 类型。

在 View 中 dispatchTouchEvent(MotionEvent event) 负责将事件分发给 onTouchEvent(MotionEvent event)来处理,简单一点理解就是下面的这段代码

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======dispatchTouchEvent==" + event.getAction());
    boolean handed = onTouchEvent(event);
    return handed; 
}
@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======onTouchEvent==" + event.getAction());
    return true;

}

通过简易模型我们可以看到:
在 dispatchTouchEvent(MotionEvent event)只要返回 true 就代表这个 View 消耗了这个事件。

如果在 onTouchEvent(MotionEvent event)中返回 true

那么这个事件的传递链就是这样的:activity 的 dispatchTouchEvent()——>ViewGroup 的 dispatchTouchEvent()——>ViewGroup onInterceptTouchEvent()——>view 的 dispatchTouchEvent() ——> view 的 onTouchEvent();

如果在 dispatchTouchEvent(MotionEvent event)中直接返回 true。类似于这样

    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i("eventTest", "=EventView======dispatchTouchEvent==" + event.getAction());
        return true;
    }

那么这个事件依旧是被这个 View 消耗了是被 dispatchTouchEvent()消耗的,并没有将事件分发给 onTouchEvent(),所以这个 View 的 onTouchEvent()不会执行,这是这个事件的传递链是这样的:activity 的 dispatchTouchEvent()——>ViewGroup 的 dispatchTouchEvent()——>ViewGroup onInterceptTouchEvent(——>view 的 dispatchTouchEvent()。

在 dispatchTouchEvent(MotionEvent event)只要返回 false 就代表这个 View 不消耗这个事件。 如果在 onTouchEvent(MotionEvent event)中返回 false,除了 action_down 以外后续事件都不会传递到这个 View 上 当然如果直接在 dispatchTouchEvent(MotionEvent event)中直接返回 false,这个 View 的 onTouchEvent()不会执行。原理同直接返回 true;

然而在现实中经常会看见这样的写法:

 @Override
 public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======dispatchTouchEvent==" + event.getAction());

    return super.dispatchTouchEvent(event);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======onTouchEvent==" + event.getAction());
    return super.onTouchEvent(event);
}

它们调用 super 的方法是因为在父类中它们各自定义了一些事件分发或者事件处理逻辑。特别是在 super.dispatchTouchEvent 中定义了将事件交给 onTouchEvent()处理的逻辑。

ViewGroup 的事件分发和处理

ViewGroup 中的事件分发出处理涉及到了三个方法: 1.public boolean dispatchTouchEvent(MotionEvent event) 2.public boolean onInterceptTouchEvent(MotionEvent ev) 3.public boolean onTouchEvent(MotionEvent event) 它们的返回值都是 boolean 类型。

在 ViewGroup 中 dispatchTouchEvent(MotionEvent event) 负责先将事件分发给自己 onInterceptTouchEvent(MotionEvent ev) 来判断自己是否拦截事件,如果 onInterceptTouchEvent(MotionEvent ev) 返回 true,表示自己要拦截这个事件,那么这个事件就会交给自己的 onTouchEvent(MotionEvent event)处理,如果事件被自己的 onTouchEvent()消耗,那么后续事件将直接交给自己的 onTouchEvent()处理,如果事件不消耗,那么 ViewGroup 的父控件不会再将事件分发过来。如果不拦截事件那么 ViewGroup 就会将事件分发给子 View,如果子 View 也不处理这个事件,那么它就会将事件交给自己的 onTouchEvent(MotionEvent event)来处理。简单一点理解就是下面的这段代码

 @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.i("eventTest", "=EventViewGroup======dispatchTouchEvent==" + ev.getAction());
    if (!inter) {
        inter = onInterceptTouchEvent(ev);
    }
    if (inter) {
        handed = onTouchEvent(ev);
    } else {
        childHanded = getChildAt(0).dispatchTouchEvent(ev);
        if (!childHanded) {
            handed = onTouchEvent(ev);
        }
    }
    return handed || childHanded;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.i("eventTest", "=EventViewGroup======onInterceptTouchEvent==" + ev.getAction());
    return false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventViewGroup======onTouchEvent==" + event.getAction());
    return false;
}

通过简易模型我们可以看到:
ViewGroup 的 onInterceptTouchEvent()则是用来直接拦截事件的,如果返回 true,就代表本 ViewGroup 要拦截事件并将事件交给自己的 onTouchEvent 处理。事件一旦被拦截 就不会再分发给当前 ViewGroup 包含的子控件了。
ViewGroup 的 dispatchTouchEvent()返回 true 就代表了这个事件是被这个 ViewGroup 消耗了。而在这个 ViewGroup 中我们知道是 ViewGroup 本身消耗了事件还是它包含的子 view 消耗了事件,从而决定了事件是分发给自己还是包含的子 view。

通过对 ViewGroup 的事件分发模型的简单化,我们可以可以看出来,如果 ViewGroup 的 dispatchTouchEvent(MotionEvent ev)直接返回 true

public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i("eventTest", "=EventView======dispatchTouchEvent==" + event.getAction());
        return true;
}

那么这个事件就会被当前的 ViewGroup 直接消耗。既不会交给自己的 onInterceptTouchEvent()以及 onTouchEvent(),也不会分发给子 view。 但是如果直接返回 false,当前 viewGroup 既不会消耗事件,也不会对事件做任何分发,但是当前 ViewGroup 会接收到 action_down 事件。

然而在现实中经常会看见这样的写法:

 @Override
 public boolean dispatchTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======dispatchTouchEvent==" + event.getAction());

    return super.dispatchTouchEvent(event);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.i("eventTest", "=EventViewGroup======onInterceptTouchEvent==" + ev.getAction());
    return super.dispatchTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=EventView======onTouchEvent==" + event.getAction());
    return super.onTouchEvent(event);
}

这是因为父 View 中,对这几个方法都有一些默认的处理逻辑。

activity 的事件分发和处理

activity 的事件分发模型和 ViewGrooup 类似,只是没有 onInterceptTouchEvent(MotionEvent ev)这一步。 可以简单概括为

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.i("eventTest", "=MainActivity======dispatchTouchEvent==" + ev.getAction());
     viewHanded = getWindow().getDecorView().findViewById(android.R.id.content).dispatchTouchEvent(ev);
    if (!viewHanded){
        handed = onTouchEvent(ev);
    }
    return handed || viewHanded;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i("eventTest", "=MainActivity======onTouchEvent==" + event.getAction());
    return super.onTouchEvent(event);
}

activity 的事件先传递给它包含的布局来处理,如果布局不处理才会交给自己的 onTouchEvent()处理。

总结

为什么在不拦截不处理事件的情况下,事件的分的过程是有 acttivity.dispatchTouchEvent(MotionEvent ev) -> viewGroup.dispatchTouchEvent(MotionEvent ev) ->onInterceptTouchEvent(MotionEvent ev)-> view.dispatchTouchEvent(MotionEvent ev) -> view.onTouchevent(otionEvent ev)->viewGroup.onTouchevent(otionEvent ev) -> activity.onTouchevent(otionEvent ev)

从对 View 和 viewGroup 以及 activity 中的事件分析我们可以看到事件的分发处理过程是这样的:首先由 activity 优先分发给他的根视图,而根视图会优先判断是否拦截事件,不拦截则对事件做继续分发,直到在某一级视图事件被消耗了才会停止分发这个事件,否则,在最后一层视图,因为它没有子视图了,那么事件的分发就结束了,从而转入对 onTouchevent()的调用,当 onTouchevent()执行结束,它的 dispatchTouchEvent(ev)执行结束即它的父视图的 getChildAt(0).dispatchTouchEvent(ev)执行结束,然后它的父视图就会判断是否调用自己的 onTouchevent()...依次类推,就有了上面的事件分发处理链。

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools