Android事件分发机制

来源:互联网 发布:美国国父路易十六知乎 编辑:程序博客网 时间:2024/06/05 20:07

ViewGroup的分发机制:

<span style="font-family:Arial;font-size:18px;">  public boolean dispatchTouchEvent(MotionEvent ev) {           调用onInterceptTouchEvent检查是否拦截事件           if(没有拦截){               在ViewGroup中遍历查找目前是点击了哪个子视图               if(找到了){                   调用该子视图的dispatchTouchEvent,递归下去               }else{                   没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent()                   如果再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,后续的move和up都被Listener或者onTouchEvent()处理,                   如果down事件返回false,则后续的move,up事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。               }            }else{               事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依然遵从上面的down和move,up事件的关系           }      }</span>

详细版本:【http://blog.csdn.net/xiaanming/article/details/21696315】

<span style="font-family:Arial;font-size:18px;">       @Override         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);         }  </span>
View的分发机制:
<span style="font-family:Arial;font-size:18px;"><span style="font-family:Arial;font-size:18px;"> public boolean dispatchTouchEvent(MotionEvent event) {      if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&              mOnTouchListener.onTouch(this, event)) {          return true;      }      return onTouchEvent(event);  } </span></span>

先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:

<span style="font-family:Arial;font-size:18px;">    public void setOnTouchListener(OnTouchListener l) {          mOnTouchListener = l;      }</span>
Bingo!找到了,mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。

第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。

第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。

View的onTouchEvent:

<span style="font-family:Arial;font-size:18px;">    public boolean onTouchEvent(MotionEvent event) {            final int viewFlags = mViewFlags;                  if ((viewFlags & ENABLED_MASK) == DISABLED) {                return (((viewFlags & CLICKABLE) == CLICKABLE ||                        (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));            }                  //如果设置了Touch代理,就交给代理来处理,mTouchDelegate默认是null            if (mTouchDelegate != null) {                if (mTouchDelegate.onTouchEvent(event)) {                    return true;                }            }                  //如果View是clickable或者longClickable的onTouchEvent就返回true, 否则返回false            if (((viewFlags & CLICKABLE) == CLICKABLE ||                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {                switch (event.getAction()) {                    case MotionEvent.ACTION_UP:                        boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;                        if ((mPrivateFlags & PRESSED) != 0 || prepressed) {                            boolean focusTaken = false;                            if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                                focusTaken = requestFocus();                            }                                  if (!mHasPerformedLongPress) {                                removeLongPressCallback();                                      if (!focusTaken) {                                    if (mPerformClick == null) {                                        mPerformClick = new PerformClick();                                    }                                    if (!post(mPerformClick)) {                                        performClick();                                    }                                }                            }                                  if (mUnsetPressedState == null) {                                mUnsetPressedState = new UnsetPressedState();                            }                                  if (prepressed) {                                mPrivateFlags |= PRESSED;                                refreshDrawableState();                                postDelayed(mUnsetPressedState,                                        ViewConfiguration.getPressedStateDuration());                            } else if (!post(mUnsetPressedState)) {                                mUnsetPressedState.run();                            }                            removeTapCallback();                        }                        break;                          case MotionEvent.ACTION_DOWN:                        if (mPendingCheckForTap == null) {                            mPendingCheckForTap = new CheckForTap();                        }                        mPrivateFlags |= PREPRESSED;                        mHasPerformedLongPress = false;                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                        break;                          case MotionEvent.ACTION_CANCEL:                        mPrivateFlags &= ~PRESSED;                        refreshDrawableState();                        removeTapCallback();                        break;                          case MotionEvent.ACTION_MOVE:                        final int x = (int) event.getX();                        final int y = (int) event.getY();                              //当手指在View上面滑动超过View的边界,                        int slop = mTouchSlop;                        if ((x < 0 - slop) || (x >= getWidth() + slop) ||                                (y < 0 - slop) || (y >= getHeight() + slop)) {                            // Outside button                            removeTapCallback();                            if ((mPrivateFlags & PRESSED) != 0) {                                removeLongPressCallback();                                      mPrivateFlags &= ~PRESSED;                                refreshDrawableState();                            }                        }                        break;                }                return true;            }                  return false;        }  </span>
推荐:

http://www.cnblogs.com/lwbqqyumidi/p/3500997.html

http://www.cnblogs.com/sunzn/archive/2013/05/10/3064129.html

http://blog.csdn.net/xiaanming/article/details/21696315





0 0