android 事件分发

来源:互联网 发布:linux上安装oracle 编辑:程序博客网 时间:2024/05/16 15:12

1.背景
在写android程序时,由于设计需要,经常要自定义控件,那么假设:有一个控件,内部可以填充子控件,满足控件的滑动且子控件可点击;是不是很常见?这个假设的实现:nice的遇见频道。你想象的,它应该是本来就可以滑动,然后子控件又可以点击,但事实不是你理解的那样。我们面临两个问题 :1.这个事件到底是如何分发和相应的2.自定义控件时,如何处理,可以完成,控件滑动,子控件可点击。
2.准备知识
Android 是怎么捕获屏幕事件的?追到源码里面window这个类,负责屏幕上事件的处理,window是怎么处理的,framework的这部分代码不开放。源码(Android 2.3)中是我们关键看这几个方法,然后再总结:
a.Activity
1.dispatchTouchEvent(MotionEvent ev);
2.onTouchEvent(MotionEvent event);

public boolean dispatchTouchEvent(MotionEvent ev) {        if (ev.getAction() == MotionEvent.ACTION_DOWN) {            onUserInteraction();//这个是处理活动状态,关,帮助活动管理状态栏通知;        }        if (getWindow().superDispatchTouchEvent(ev){    //window实质上来完成事件的分发            return true;        }        return onTouchEvent(ev);    }public boolean onTouchEvent(MotionEvent event) {     // 默认是不消费事件的        return false;    }

b.ViewGroup
1.dispatchTouchEvent(MotionEvent ev);//viewGroup分发事件,有些长
2.onTouchEvent(MotionEvent event);//真正处理事件的
3.onInterceptTouchEvent(MotionEvent ev);//截断事件传递

 /**     * {@inheritDoc}     */    @Override    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;       //是否允许截断        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;        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.                mMotionTarget = null;            }            // If we're disallowing intercept or if we're allowing and we didn't            // intercep 两个参数控制            if (disallowIntercept || !onInterceptTouchEvent(ev)) {                // reset this event's action (just to protect ourselves)                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;                final View[] children = mChildren;                final int count = mChildrenCount;              // 遍历,检查那个子控件落在点击位置上                for (int i = count - 1; i >= 0; i--) {                    final View child = children[i];                    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;                            if (child.dispatchTouchEvent(ev))  {                                // Event handled, we have a target now.                                mMotionTarget = child;                                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;        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;        }         //交给子控件的分发处理          //注意整个过程没有看到调用ViewGroup自己的ontouchEvent方法        return target.dispatchTouchEvent(ev);    }     //默认是不拦截事件分发的 public boolean onInterceptTouchEvent(MotionEvent ev) {        return false;    }

c.View
1.dispatchTouchEvent(MotionEvent event);
2.onTouchEvent(MotionEvent event);

 public boolean dispatchTouchEvent(MotionEvent event) {        if (!onFilterTouchEventForSecurity(event)) {            return false;        }        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&                mOnTouchListener.onTouch(this, event)) {            return true;        }        return onTouchEvent(event);    }

3查看源码结论:

这里写图片描述

4自定义控件中,如何处理,滑动和子控件的点击?下篇

0 0