Touch事件分发中的角色
Touch事件分发中有两个主角:ViewGroup和View。
Activity的Touch事件事实上是调用它内部的ViewGroup的Touch事件,可以直接当成ViewGroup处理。
View在ViewGroup内,ViewGroup也可以在其他ViewGroup内,这时候把内部的ViewGroup当成View来分析。
ViewGroup与View的相关事件:
ViewGroup的相关事件有三个:
- onInterceptTouchEvent
- dispatchTouchEvent
- onTouchEvent
View的相关事件只有两个:
- dispatchTouchEvent
- onTouchEvent。
ViewGroup的处理流程的树形结构模型:
ViewGroup和View组成了一棵树形结构,最顶层为Activity的ViewGroup,下面有若干的ViewGroup节点,每个节点之下又有若干的ViewGroup节点或者View节点,依次类推。如图:
当触摸发生时,由上至下传递。
下发的过程是:
调用**子View(ViewGroup)**的dispatchTouchEvent方法实现的1。
上述例子中的消息下发顺序是这样的:①-②-⑤-⑥-⑦-③-④。
dispatchTouchEvent方法只负责事件的分发,它拥有boolean类型的返回值,当返回为true时,顺序下发会中断。在上述例子中如果⑤的dispatchTouchEvent返回结果为true,那么⑥-⑦-③-④将都接收不到本次Touch事件。
来个简单版的代码加深理解:
/**
* ViewGroup
* @param ev
* @return
*/
public boolean dispatchTouchEvent(MotionEvent ev){
....//其他处理,在此不管
View[] views=getChildView();
for(int i=0;i<views.length;i++){
//判断下Touch到屏幕上的点在该子View上面
if(...){
if(views[i].dispatchTouchEvent(ev))
return true;
}
}
...//其他处理,在此不管
}
/**
* View
* @param ev
* @return
*/
public boolean dispatchTouchEvent(MotionEvent ev){
....//其他处理,在此不管
return false;
}
在此可以看出,ViewGroup的dispatchTouchEvent是真正在执行“分发”工作,而View的dispatchTouchEvent方法,并不执行向下分发工作。
事实上子View的dispatchTouchEvent方法真正执行的代码是这样的
/**
* View
* @param ev
* @return
*/
public boolean dispatchTouchEvent(MotionEvent ev){
....//其他处理,在此不管
return onTouchEvent(event);
}
子View的dispatchTouchEvent只是决定是否把touch事件交给自己处理,而处理的方法,便是onTouchEvent事件。
所以,一般情况下,我们不该在普通View内重写dispatchTouchEvent方法,因为它并不执行分发逻辑。当Touch事件到达View时,我们该做的就是是否在onTouchEvent事件中处理它。
ViewGroup的onTouchEvent事件是什么时候处理的呢?
当ViewGroup所有的子View都返回false时,onTouchEvent事件便会执行。由于ViewGroup是继承于View的,它其实也是通过调用View的dispatchTouchEvent方法来执行onTouchEvent事件(可以看Android源码)。
Aciton_UP和Action_MOVE什么时候执行?
当某个View或者ViewGroup的onTouchEvent事件返回true时,便表示它是真正要处理这次请求的View,之后的Aciton_UP和Action_MOVE将由它处理。当所有子View的onTouchEvent都返回false时,这次的Touch请求就由根ViewGroup,即Activity自己处理了。
看看改进后的ViewGroup的dispatchTouchEvent方法
View mTarget=null;//保存捕获Touch事件处理的View
public boolean dispatchTouchEvent(MotionEvent ev) {
//....其他处理,在此不管
if(ev.getAction()==KeyEvent.ACTION_DOWN){
//每次Down事件,都置为Null
if(!onInterceptTouchEvent()){
mTarget=null;
View[] views=getChildView();
for(int i=0;i<views.length;i++){
if(views[i].dispatchTouchEvent(ev))
mTarget=views[i];
return true;
}
}
}
//当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move
if(mTarget==null){
return super.dispatchTouchEvent(ev);
}
//...其他处理,在此不管
if(onInterceptTouchEvent()){
//...其他处理,在此不管
}
//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
return mTarget.dispatchTouchEvent(ev);
}
ViewGroup还有个onInterceptTouchEvent,看名字便知道这是个拦截事件。这个拦截事件需要分两种情况来说明:
1当mTarge一直为null,该ViewGroup的onTouchEvent事件被执行。这种情况下可以把这个ViewGroup直接当成View来对待。
比如:.假如我们在某个ViewGroup的onInterceptTouchEvent中,将Action为Down的Touch事件返回true,那便表示将该ViewGroup的所有下发操作拦截掉,这种情况下,mTarget会一直为null,因为mTarget是在Down事件中赋值的。
另外,上文所列出的代码并非真正的源码,只是概括了源码在事件分发处理中的核心处理流程,真正源码各位可以自己去看,包含了更丰富的内容。
###补充:
“触摸事件由Action_Down、Action_Move、Aciton_UP组成,其中一次完整的触摸事件中,Down和Up都只有一个,Move有若干个,可以为0个。”,这里补充下其实UP事件是可能为0个的。
转载:http://www.cnblogs.com/linjzong/p/4191891.html
- 简单来说,就是ViewGroup遍历它包含着的子View,调用每个View的dispatchTouchEvent方法,而当子View为ViewGroup时,又会通过调用ViwGroup的dispatchTouchEvent方法继续调用其内部的View的dispatchTouchEvent方法。 ↩