android事件分发机制详解

来源:互联网 发布:淘宝 香港 编辑:程序博客网 时间:2024/04/29 14:38

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咯。


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

ViewGroup1

<span style="font-family:Microsoft YaHei;font-size:14px;">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 * 邮箱:fangjaylong@gmail.com * 创建时间: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);    }}</span>

ViewGroupe2

<span style="font-family:Microsoft YaHei;font-size:14px;">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 * 邮箱:fangjaylong@gmail.com * 创建时间: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);    }}</span>


View


<span style="font-family:Microsoft YaHei;font-size:14px;">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 * 邮箱:fangjaylong@gmail.com * 创建时间: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);    }}</span>


然后在Activity中使用他们


<span style="font-family:Microsoft YaHei;font-size:14px;">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);    }}</span>

效果如下图所示



当我们点击红色区域时可以看到如下的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了

<span style="font-size:14px;">  @Override    public boolean onTouchEvent(MotionEvent event) {        Log.d("View", "onTouchEvent");return  true;//        return super.onTouchEvent(event);    }</span>


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了

<pre name="code" class="java">    @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时事件将不会再继续往上传递。



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


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



0 0
原创粉丝点击