事件之分发总结
17.android事件处理涉及到的三个重要函数
16.View事件处理机制核心代码
14.ViewGroup事件处理机制核心代码
11.Android中的事件分类:
----------
17.android事件处理涉及到的三个重要函数
16.View事件处理机制核心代码
14.ViewGroup事件处理机制核心代码
Android中诸如LinearLayout等的五大布局控件,都是继承自ViewGroup,而ViewGroup本身是继承自View,所以ViewGroup的事件处理机制对这些控件都有效。
总结:
1、dispatchTouchEvent作用:决定事件是否由onInterceptTouchEvent来拦截处理。
返回super.dispatchTouchEvent时,由onInterceptTouchEvent来决定事件的流向;
返回true时,不会继续分发事件,自己内部处理了所有事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP);
返回false时,会继续分发事件,自己内部只处理了ACTION_DOWN;
2、onInterceptTouchEvent作用:拦截事件,用来决定事件是否传向子View
返回true时,拦截后交给自己的onTouchEvent处理;
返回false时,拦截后交给子View来处理;
3、onTouchEvent作用:事件最终到达这个方法。
返回true时,内部处理所有的事件,换句话说,后续事件将继续传递给该view的onTouchEvent()处理
返回false时,事件会向上传递,由onToucEvent来接受,如果最上面View中的onTouchEvent也返回false的话,那么事件就会消失;
ViewGroup.java(基于android2.3.3):
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
//onInterceptTouchEvent返回false,说明向下传递
//onInterceptTouchEvent返回true,说明拦截
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
...
// 伪代码如下:
//1,找到当前控件子控件
//2,判断当前touch的点的坐标(x,y)在哪个子控件的矩形区域内
//3,判断当前子控件是viewgroup的子类对象,还是view的子类对象
//3.1 如果是viewgroup的子类: 调用其dispatchTouchEvent方法,上述操作再来一遍
//3.2 view 尝试让当前view去处理这个事件(
true,dispatchTouchEvent方法结束,并且返回true
false,dispatchTouchEvent继续向下执行)
...0
}
}
...
target = mMotionTarget
//target一定是null
if (target == null) {
...
//调用当前viewgroup的父View的处理事件的方法
return super.dispatchTouchEvent(ev);
}
...
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;// 默认返回false
}
4.ViewGroup源码分析
Android中诸如LinearLayout等的五大布局控件,都是继承自ViewGroup,而ViewGroup本身是继承自View,所以ViewGroup的事件处理机制对这些控件都有效。
部分源码:
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
//这个值默认是false, 然后我们可以通过requestDisallowInterceptTouchEvent(boolean disallowIntercept)方法
//来改变disallowIntercept的值
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//这里是ACTION_DOWN的处理逻辑
if (action == MotionEvent.ACTION_DOWN) {
//清除mMotionTarget, 每次ACTION_DOWN都很设置mMotionTarget为null
if (mMotionTarget != null) {
mMotionTarget = null;
}
//disallowIntercept默认是false, 就看ViewGroup的onInterceptTouchEvent()方法
if (disallowIntercept || !onInterceptTouchEvent(ev)) { //第一点
ev.setAction(MotionEvent.ACTION_DOWN);
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
//遍历其子View
for (int i = count - 1; i >= 0; i--) { //第二点
final View child = children[i];
//如果该子View是VISIBLE或者该子View正在执行动画, 表示该View才
//可以接受到Touch事件
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
//获取子View的位置范围
child.getHitRect(frame);
//如Touch到屏幕上的点在该子View上面
if (frame.contains(scrolledXInt, scrolledYInt)) {
// offset the event to the view's coordinate system
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
//调用该子View的dispatchTouchEvent()方法
if (child.dispatchTouchEvent(ev)) {
// 如果child.dispatchTouchEvent(ev)返回true表示
//该事件被消费了,设置mMotionTarget为该子View
mMotionTarget = child;
//直接返回true
return true;
}
// The event didn't get handled, try the next view.
// Don't reset the event's location, it's not
// necessary here.
}
}
}
}
}
//判断是否为ACTION_UP或者ACTION_CANCEL
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL);
if (isUpOrCancel) {
//如果是ACTION_UP或者ACTION_CANCEL, 将disallowIntercept设置为默认的false
//假如我们调用了requestDisallowInterceptTouchEvent()方法来设置disallowIntercept为true
//当我们抬起手指或者取消Touch事件的时候要将disallowIntercept重置为false
//所以说上面的disallowIntercept默认在我们每次ACTION_DOWN的时候都是false
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}
// The event wasn't an ACTION_DOWN, dispatch it to our target if
// we have one.
final View target = mMotionTarget;
//mMotionTarget为null意味着没有找到消费Touch事件的View, 所以我们需要调用ViewGroup父类的
//dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法
if (target == null) {
// We don't have a target, this means we're handling the
// event as a regular view.
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
return super.dispatchTouchEvent(ev);
}
//这个if里面的代码ACTION_DOWN不会执行,只有ACTION_MOVE
//ACTION_UP才会走到这里, 假如在ACTION_MOVE或者ACTION_UP拦截的
//Touch事件, 将ACTION_CANCEL派发给target,然后直接返回true ,表示消费了此Touch事件
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
}
// clear the target
mMotionTarget = null;
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;
}
if (isUpOrCancel) {
mMotionTarget = null;
}
// finally offset the event to the target's coordinate system and
// dispatch the event.
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc);
if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
}
//如果没有拦截ACTION_MOVE, ACTION_DOWN的话,直接将Touch事件派发给target
return target.dispatchTouchEvent(ev);
}
11.Android中的事件分类:
Touch事件,四种状态: ACTIONDOWN ??> 表示按下了屏幕,一个事件必然从ACTIONDOWN开始
ACTION_MOVE ??> 表示移动手势
ACTION_UP ??> 表示离开屏幕
ACTION_CANCEL ??> 表示取消手势,一般由程序产生,不会由用户产生。
一个ACTIONDOWN, n个ACTIONMOVE,1个ACTION_UP,就构成了Android中众多的事件。 Android中的事件onClick, onScroll, onFling等等,都是由许多个Touch组成的。一个原则,所有的touch事件都是从父容器开始向下传递的,呈U字形。
1.涉及到事件响应的常用方法构成
用户在手指与屏幕接触过程中通过MotionEvent对象产生一系列事件,它有四种状态:
MotionEvent.ACTION_DOWN:手指按下屏幕的瞬间(一切事件的开始);
MotionEvent.ACTIONMOVE:手指在屏幕上移动;
MotionEvent.ACTIONUP:手指离开屏幕瞬间; MotionEvent.ACTION_CANCEL :取消手势,一般由程序产生,不会由用户产生;
Android中的事件onClick, onLongClick,onScroll, onFling等等,都是由许多个Touch事件构成的(一个ACTIONDOWN, n个ACTIONMOVE,1个ACTION_UP)。
android 事件响应机制是先 分发(先由外部的View接收,然后依次传递给其内层的最小View)再 处理 (从最小View单元(事件源)开始依次向外层传递。)的形式实现的。
复杂性表现在:可以控制每层事件是否继续传递(分发和拦截协同实现),以及事件的具体消费(事件分发也具有事件消费能力)。