做了这么多年的android项目,经常会遇到事件冲突的问题,比如scrollview里面添加listview展不开,item里面有可点击的button时item点击无效,ripple的波纹效果和点击事件冲突等等吧。为了很好的解决这类问题,也为了更好的理解android源码,我打算由浅入深的研究一下android的事件分发机制。
事件的分类:触摸,点击,长按,拖动,缩放。
基本应用
//触摸
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "onTouch execute, action " + event.getAction());
return false;
}
});
//点击
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "onClick execute");
}
});
//长按
tv.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// TODO Auto-generated method stub
return false;
}
});
//拖曳,放大
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
}
当然也可以用gestureDetector类。
观察发现返回值都是boolean类型,有时候是true,有时候为false。
看下demo
tv.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("", "touch--");
return true;
}
});
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e("", "click--");
}
});
如果 touch事件返回ture,那么click事件是没有执行的;如果 touch事件返回false,那么click事件是执行了的。
可以看到,onTouch是优先于onClick执行的,并且onTouch执行了两次,一次是ACTION_DOWN,一次是ACTION_UP(你还可能会有多次ACTION_MOVE的执行,如果你手抖了一下)。
因此事件传递的顺序是先经过onTouch,再传递到onClick。如果 返回true那么这次事件将会被消耗掉。
事件传递是从parentview,传递到的childview。demo很简单就不写了。
在来点深入的。来看看源码
源码中三个核心方法
dispatchTouchEvent
onInterceptTouchEvent
onTouchEvent
从头到尾总结一下:
1.Touch事件分发中只有两个主角:ViewGroup和View。ViewGroup包含onInterceptTouchEvent、dispatchTouchEvent、onTouchEvent三个相关事件。View包含dispatchTouchEvent、onTouchEvent两个相关事件。其中ViewGroup又继承于View。
2.ViewGroup和View组成了一个树状结构,根节点为Activity内部包含的一个ViwGroup。
3.触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。
4.当Acitivty接收到Touch事件时,将遍历子View进行Down事件的分发。ViewGroup的遍历可以看成是递归的。分发的目的是为了找到真正要处理本次完整触摸事件的View,这个View会在onTouchuEvent结果返回true。
5.当某个子View返回true时,会中止Down事件的分发,同时在ViewGroup中记录该子View。接下去的Move和Up事件将由该子View直接进行处理。由于子View是保存在ViewGroup中的,多层ViewGroup的节点结构时,上级ViewGroup保存的会是真实处理事件的View所在的ViewGroup对象:如ViewGroup0-ViewGroup1-TextView的结构中,TextView返回了true,它将被保存在ViewGroup1中,而ViewGroup1也会返回true,被保存在ViewGroup0中。当Move和UP事件来时,会先从ViewGroup0传递至ViewGroup1,再由ViewGroup1传递至TextView。
6.当ViewGroup中所有子View都不捕获Down事件时,将触发ViewGroup自身的onTouch事件。触发的方式是调用super.dispatchTouchEvent函数,即父类View的dispatchTouchEvent方法。在所有子View都不处理的情况下,触发Acitivity的onTouchEvent方法。
7.onInterceptTouchEvent有两个作用:1.拦截Down事件的分发。2.中止Up和Move事件向目标View传递,使得目标View所在的ViewGroup捕获Up和Move事件。