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


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


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值