android事件分发机制详解

android系统的点击事件是由最初的硬件触发的,然后 传递给屏幕最外缘的ViewGroup 继而往下传递,直到最底层的View然后向上回传。

整个过程可以分为: 

viewGroup-->dispatchTouchEvent()

 viewgroup-->onInterceptTouchEvent()

……

……

view-->dispatchTouchEvent()

view-->onTouchEvent()

viewGroup-->onTouchEvent()

简单说下几个方法的作用:


dispatchTouchEvent()      主要负责事件的传递

onInterceptTouchEvent()  主要负责事件的拦截

onTouchEvent()                主要负责事件的处理


在一个ViewGroup中可以包含上述的三种方法,包括事件的传递、拦截和处理。而在一个view中只有事件的传递和事件的处理而没有事件的拦截方法,因为view里面无法包含其他view了哦。


一个点击事件默认是从dispatchTouchEvent()    开始的然后由onInterceptTouchEvent() 做拦截处理向下传递。

 
  
@Override
public boolean dispatchTouchEvent (MotionEvent ev) {
Log. d( "ViewGroup1" , "dispatchTouchEvent") ;
return super .dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent (MotionEvent ev) {
Log. d ( "ViewGroup1" , "onInterceptTouchEvent" ) ;
return super .onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent (MotionEvent event) {
Log. d ( "ViewGroup1" , "onTouchEvent" ) ;
return super .onTouchEvent(event);
}

1.三个方法的默认返回值都为false,都是默认不做拦截和处理。

2.当spatchTouchEvent()  返回true时,当前的事件在当前的View(ViewGroup)中的spatchTouchEvent()  中处理,不再向下做任何处理,即事件在此处结束。

3.当spatchTouchEvent()  返回false,而且onInterceptTouchEvent()也返回false时,事件回向下继续传递。

spatchTouchEvent()  返回false,而且onInterceptTouchEvent()也返回true时,事件将会交给此处View(ViewGroup)的onTouchEvent()    处理。

4.当事件交给onTouchEvent()  后,事件将不会再往下继续传递,onTouchEvent()    的返回值决定事件是否继续往上传递,如果onTouchEvent()    返回false则事件可以继续向上传递,上层的viewGroup可以决定是否对事件进行处理。

onTouchEvent()    返回值为true时事件将不会再继续往上传递。


看了这么多字相信大家都会觉得很绕,那么下面用一个例子说明下。

故事篇:可怜的小明

在学校班主任向班长交代了一些打扫学习的任务,班长自己不想做就把这些任务交给学生小明,小明没办法就的去完成任务啊,小明很负责地完成任务后,把完成任务的消息告诉了班长,班长最后告诉了班主任,最后班主任很高兴地夸奖了班长。


好了,听了刚才的故事,我们可以把班主任当作为最外层的ViewGroup把班长当作内层的ViewGroup,小明就只能是最内层的View咯。

android事件分发机制详解_第1张图片

我们可以自定义一个ViewGroup1作为班主任,VeiwGroup2作为班长,自定义view作为可怜的小明,ViewGroup分别重写里面的dispatchTouchEvent() ,onInterceptTouchEvent()  和onTouchEvent()  方法,而View则重写dispatchTouchEvent() ,和onTouchEvent()  方法。

ViewGroup1

package com.flyou.touchmode;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * ============================================================
 * 项目名称:OrmListeDemo
 * 包名称:com.flyou.touchmode
 * 文件名:ViewGroup1
 * 类描述:
 * 创建人:flyou
 * 邮箱:[email protected]
 * 创建时间:2015/9/16 14:51
 * 修改备注:
 * 版本:@version  V1.0
 * ============================================================
 */
public class ViewGroup1 extends LinearLayout {
    public ViewGroup1(Context context) {
        super(context);
    }

    public ViewGroup1(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroup1(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ViewGroup1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("ViewGroup1", "dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);

    }

    @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("ViewGroup1", "onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("ViewGroup1", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}

ViewGroupe2

package com.flyou.touchmode;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.LinearLayout;

/**
 * ============================================================
 * 项目名称:OrmListeDemo
 * 包名称:com.flyou.touchmode
 * 文件名:ViewGroup2
 * 类描述:
 * 创建人:flyou
 * 邮箱:[email protected]
 * 创建时间:2015/9/16 14:54
 * 修改备注:
 * 版本:@version  V1.0
 * ============================================================
 */
public class ViewGroup2 extends LinearLayout {
    public ViewGroup2(Context context) {
        super(context);
    }

    public ViewGroup2(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ViewGroup2(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ViewGroup2(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("ViewGroup2", "dispatchTouchEvent");
      return super.dispatchTouchEvent(ev);

    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("ViewGroup2", "onInterceptTouchEvent");

        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("ViewGroup2", "onTouchEvent");

     return super.onTouchEvent(event);
    }
}


View


package com.flyou.touchmode;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

/**
 * ============================================================
 * 项目名称:OrmListeDemo
 * 包名称:com.flyou.touchmode
 * 文件名:View
 * 类描述:
 * 创建人:flyou
 * 邮箱:[email protected]
 * 创建时间:2015/9/16 14:55
 * 修改备注:
 * 版本:@version  V1.0
 * ============================================================
 */
public class View  extends android.view.View{
    public View(Context context) {
        super(context);
    }

    public View(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public View(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("View", "dispatchTouchEvent");

   return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("View", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}


然后在Activity中使用他们


package com.flyou.touchmode;

import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View view =new View(MainActivity.this);
        view.setBackgroundColor(Color.RED);
        ViewGroup2 viewGroup2=new ViewGroup2(MainActivity.this);
        viewGroup2.setBackgroundColor(Color.BLUE);
        viewGroup2.addView(view, 200, 200);

        ViewGroup1 viewGroup1=new ViewGroup1(MainActivity.this);
        viewGroup1.setBackgroundColor(Color.GREEN);
        viewGroup1.addView(viewGroup2, 350, 350);
        setContentView(viewGroup1);
    }


}

效果如下图所示


android事件分发机制详解_第2张图片


当我们点击红色区域时可以看到如下的Log


09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup1﹕ dispatchTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup1﹕ onInterceptTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup2﹕ dispatchTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup2﹕ onInterceptTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/View﹕ dispatchTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/View﹕ onTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup2﹕ onTouchEvent
09-16 17:27:45.223  24157-24157/com.flyou.touchmode D/ViewGroup1﹕ onTouchEvent


从上述的log可以很清楚的看出,事件先有最外层的ViewGroup1的接受并往下传递,由最底层的view先开始处理,并往上传递。


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到这里,相信大家对事件的传递机制已经有了一定的了解,可是小明的故事并没有结束,准确的说是还有其他情况。


故事番外篇:良心发现的班长

班长从班主任手中接过任务,自己本来还想交给小明来做的,可是突然间良心发现觉得总是欺负小明不好,于上就自己把校园给打扫了。


这样事件到班长就没有往下传递了,而是由班长完成再告诉班主任。


那么在我们的例子中应该怎么做呢?

从上述的3、4条我们可以得出,只需要将viewgroup2的onInterceptTouchEvent返回值 设置为true就ok了,这样子事件就不会在继续向下传递了。

   @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("ViewGroup2", "onInterceptTouchEvent");
        return true;
//        return super.onInterceptTouchEvent(ev);
    }


点击后得到如下输出。

09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup1﹕ dispatchTouchEvent
09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup1﹕ onInterceptTouchEvent
09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup2﹕ dispatchTouchEvent
09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup2﹕ onInterceptTouchEvent
09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup2﹕ onTouchEvent
09-16 17:38:01.911    5671-5671/com.flyou.touchmode D/ViewGroup1﹕ onTouchEvent


由上述的例子,我们可以很清楚的看出,onInterceptTouchEvent的返回值决定了是否对事件进行拦截处理。


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到这里想必大家对事件的传递机制有了进一步的了解,但是小明的故事并没有结束,让我们继续看看


故事番外篇:小明的愤怒

这天班长又来给小明交代任务了,小明很清楚的知道这任务是班主任交给班长做的,不是给自己的,小明很生气,但是又不敢不做啊,于是就闷头把任务做完了,但是小明越想越生气,最后一气之下就没有把做完的事情告诉班长。然后班长也没法告诉班主任任务完成了,然后班长就挨骂了,哈哈。


从上面的故事我们可以很清楚的看到是View自己把事件给处理了,并且没有交给父类控件处理,有上面的3、4条我们很容易得出方案。

只需要将onTouchEvent的返回值改为true就ok了

  @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("View", "onTouchEvent");
return  true;
//        return super.onTouchEvent(event);
    }


09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/ViewGroup1﹕ dispatchTouchEvent
09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/ViewGroup1﹕ onInterceptTouchEvent
09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/ViewGroup2﹕ dispatchTouchEvent
09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/ViewGroup2﹕ onInterceptTouchEvent
09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/View﹕ dispatchTouchEvent
09-16 17:48:42.447  15652-15652/com.flyou.touchmode D/View﹕ onTouchEvent

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到这里想必大家已经非常熟悉事件的分发机制了,最后我们就来说说dispatchTouchEvent的用法,dispatchTouchEvent是用来传递消息的,如果返回false则默认向下传递,如果返回true,则直接拦截被自己处理。


故事番外篇:小明不干了

经过上一次的事件之后,班长挨了班主任的骂,班长对小明记恨在心,出处为难小明,这天班主任又给班长打扫厕所的任务,班长当然不会自家干了啊,于是就像到了小明,便把打扫厕所的事交给了小明,小明一听,艹 老子不干,再也不干了!!!


又上述可见,小明是没有对事件进行处理的,班长、和班主任也没有得到事件的反馈,事件到小明这里就结束了。


又上述的第2条,我们可以很容易的只要将View的 dispatchTouchEvent的返回值设为true就ok了

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("View", "dispatchTouchEvent");
return  true;
//   return super.dispatchTouchEvent(event);
    }

 
  

09-16 17:56:52.679  23564-23564/com.flyou.touchmode D/ViewGroup1﹕ dispatchTouchEvent
09-16 17:56:52.679  23564-23564/com.flyou.touchmode D/ViewGroup1﹕ onInterceptTouchEvent
09-16 17:56:52.679  23564-23564/com.flyou.touchmode D/ViewGroup2﹕ dispatchTouchEvent
09-16 17:56:52.679  23564-23564/com.flyou.touchmode D/ViewGroup2﹕ onInterceptTouchEvent
09-16 17:56:52.679  23564-23564/com.flyou.touchmode D/View﹕ dispatchTouchEvent


还是上面的几点:

1.三个方法的默认返回值都为false,都是默认不做拦截和处理。

2.当spatchTouchEvent()  返回true时,当前的事件在当前的View(ViewGroup)中的spatchTouchEvent()  中处理,不再向下做任何处理,即事件在此处结束。

3.当spatchTouchEvent()  返回false,而且onInterceptTouchEvent()也返回false时,事件回向下继续传递。

当spatchTouchEvent()  返回false,而且onInterceptTouchEvent()也返回true时,事件将会交给此处View(ViewGroup)的onTouchEvent()    处理。

4.当事件交给onTouchEvent()  后,事件将不会再往下继续传递,onTouchEvent()    的返回值决定事件是否继续往上传递,如果onTouchEvent()    返回false则事件可以继续向上传递,上层的viewGroup可以决定是否对事件进行处理。

onTouchEvent()    返回值为true时事件将不会再继续往上传递。



就这样,小明最后逃出了班长的恶爪,赢取了白富美,过上了幸福的生活。


好了,这就是小明的故事……



你可能感兴趣的:(从零开始学android,事件的分发机制,风飞雪未扬,小明逆袭计)