android系统的点击事件是由最初的硬件触发的,然后 传递给屏幕最外缘的ViewGroup 继而往下传递,直到最底层的View然后向上回传。
整个过程可以分为:
viewGroup-->dispatchTouchEvent()
viewgroup-->onInterceptTouchEvent()
……
……
view-->dispatchTouchEvent()
view-->onTouchEvent()
viewGroup-->onTouchEvent()
简单说下几个方法的作用:
dispatchTouchEvent() 主要负责事件的传递
onInterceptTouchEvent() 主要负责事件的拦截
onTouchEvent() 主要负责事件的处理
在一个ViewGroup中可以包含上述的三种方法,包括事件的传递、拦截和处理。而在一个view中只有事件的传递和事件的处理而没有事件的拦截方法,因为view里面无法包含其他view了哦。
一个点击事件默认是从dispatchTouchEvent() 开始的然后由onInterceptTouchEvent() 做拦截处理向下传递。
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
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);
}
}
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);
}
}
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);
}
}
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);
}
}
当我们点击红色区域时可以看到如下的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时事件将不会再继续往上传递。
就这样,小明最后逃出了班长的恶爪,赢取了白富美,过上了幸福的生活。
好了,这就是小明的故事……