一.手点击的时间是从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