您的位置:首页 > 移动开发 > Android开发

Android事件分发完全解析(自己)

2015-09-20 15:16 609 查看


一.手点击的时间是从activity开始的,先看activity的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {

if (ev.getAction() == MotionEvent.ACTION_DOWN) {

onUserInteraction();

}

if (getWindow().superDispatchTouchEvent(ev)) {

return true;

}

return onTouchEvent(ev);

二.window中superDispatchTouchEvent是抽象的









这个是ViewGroup.java中的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
}

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;
//disallowIntercept这个变量可以通过getParent.requestParentdisallowIntercept();改变
//mMotionTarget这个是用来记录子View的
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
// this is weird, we got a pen down, but we thought it was
// already down!
// XXX: We should probably send an ACTION_UP to the current
// target.
//不为空肯定是不正常的,因为一个事件是由DOWN开始的,而DOWN还没有被消费,所以目标也不是不可能被确定,
         //造成这个的原因可能是在上一次up事件或者cancel事件的时候,没有把目标赋值为空
mMotionTarget = null;
}
// If we're disallowing intercept or if we're allowing and we didn't
// intercept
//有些控件会重新onInterceptTouchEvent写在某些条件下会return true,原生的是return false
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// reset this event's action (just to protect ourselves)
//这里就发送一个Action_down事件
ev.setAction(MotionEvent.ACTION_DOWN);
// We know we want to dispatch the event down, find a child
// who can handle it, start with the front-most child.
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
//这里就获取所有子view存到数组children 里面
final View[] children = mChildren;
final int count = mChildrenCount;
//这里倒着迭代的,所以后面的是蓝色先响应,灰色后响应
//所以这就是为什么一个ViewGroup中addview(A或者B),B盖在A的区域里面,所以点击B的时候是先响应B(A,B是兄弟关系)
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
//这里就是view必须可见才能进去,如果child可见,或者有动画,获取该child的矩阵(child.getHitRect(frame);)
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
//获取矩形区域的位置
child.getHitRect(frame);
//判断手指是否在矩形区域内
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;
//走到这里就会判断child是ViewGroup还是view,如果是viewgroup又重新从上到下走一遍
//这里的child上面用了多态的方式,可能是view,也可能是viewgroup(都会重新走一遍dispatchTouchEvent,只是走的是viewgroup还是view的区别)
if (child.dispatchTouchEvent(ev))  {
// Event handled, we have a target now.
mMotionTarget = child;
</pre><pre name="code" class="java">//如果走到这里就跳出去,准备执行Action_up事件,前面有个判断是否为Action_down,所以不会走这一大段,直接走下面的action_up(谁给的Action_UP????底层写的)
//判断当前的子view的返回值,如果返回为true,那么就记录子view,在Up的时候分发给当前记录的view,(这里讲下为什么可点击的在ontouch中return false,还可以接受到up//,因为下面的OnTouchEvent判断是view是可点击的(所以会在onTouchEvent中返回一个true),child.dispatchTouchEvent(ev)就返回true,这样即使在ontouch中返回false,//也会走下面OnTouchevent返回true)(然后可以打印actionup是因为跑了最后的target.dispatchTouch,没有跑action_up是因为跑了super.dispathcTouch(也就是View(因为我//们这里指定的都是imageview或者button,他们父类都是view(button跑了target.dispath,imageview跑了系统的view.dispatch),所以就跑了系统默认的dispatchTouchevent,没有跑我们复写的dispatchTouchevent)))
return true;
}
// The event didn't get handled, try the next view.
// Don't reset the event's location, it's not
// necessary here.
}
}
}
}
}

boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL);

if (isUpOrCancel) {
// Note, we've already copied the previous state to our local
// variable, so this takes effect on the next event
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}

// The event wasn't an ACTION_DOWN, dispatch it to our target if
// we have one.
final View target = mMotionTarget;
//这里就判断子view有没有存,如果存了,就是跑进了上面的child.dispatchTouchEvent(ev),如果没存说明就没进去
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 have a target, see if we're allowed to and want to intercept its
// events
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)) {
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// 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_down打印了,难道action_up是在这个地方打印????应该是,前面targe为空的时候,return super.dispatchTouchEvent
return target.dispatchTouchEvent(ev);
}


,下面举个例子





2.







现在把FrameLayout的ontouch事件改一下return true 之前是 return false





点击红色区域



在Action_down事件中已经记录了子view,然后在action_up中就不会再跑onInterceptTouchEvent事件,不会拦截(因为onInterceptTouchEvent只在action_down中调用)

现在是点击灰色区域



第二个坐标打印不是action_up调用,而是判断当前子view为空 target为空里面return super.dispatchOnTouch,也就是viewgroup的父类的view.dispatchonTouch,然后调用view的dispatchTouch函数(之前设置OnTouch里面return true),

这里的dispatchTouchEvent是window的dispathchTouchEvent调用的

public boolean dispatchTouchEvent(MotionEvent ev) {

System.out.println("x轴坐标---" + ev.getX() + "--y轴坐标---" + ev.getY());

System.out.println("FrameLayout---dispatchTouchEvent--start");

boolean b = super.dispatchTouchEvent(ev);

System.out.println("FrameLayout---dispatchTouchEvent--end--" + b);

return b;

}



现在给linearlayout设置ontouch设置return true
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: