Android事件分发之ViewGroup(二)

ViewGroup的dispatchTouchEvent事件总线路:

   
   
  1. public boolean dispatchTouchEvent(MotionEvent ev) {
  2. 调用onInterceptTouchEvent检查是否拦截事件
  3. if(没有拦截){
  4. ViewGroup中遍历查找目前是点击了哪个子视图
  5. if(找到了){
  6. 调用该子视图的dispatchTouchEvent,递归下去
  7. }else{
  8. 没找到,则将事件传给onTouchListener,没有Listener则传给onTouchEvent()
  9. 如果再listener或者onTouchEvent()中down事件返回了true,代表事件被消费,后续的moveup都被Listener或者onTouchEvent()处理,
  10. 如果down事件返回false,则后续的moveup事件将不会到这一层的Viewgroup,而直接在上一层视图被消费。
  11. }
  12. }else{
  13. 事件被拦截了,原本被点击的子视图将接收到一个ACTION_CANCEL事件,而down事件传给onTouchListener,没有Listener则传给onTouchEvent(),依然遵从上面的downmove,up事件的关系
  14. }
  15. }
ViewGroup的 onInterceptTouchEvent方法默认返回的是false,表示当前ViewGroup不对事件进行拦截;返回true表示对事件进行拦截,外面一层的子View将不会得到该事件的响应。

1.ViewGroup的dispatchTouchEvent中的ACTION_DOWN

   
   
  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. if (!onFilterTouchEventForSecurity(ev)) {
  4. return false;
  5. }
  6. final int action = ev.getAction();
  7. final float xf = ev.getX();
  8. final float yf = ev.getY();
  9. final float scrolledXFloat = xf + mScrollX;
  10. final float scrolledYFloat = yf + mScrollY;
  11. final Rect frame = mTempRect;
  12. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  13. if (action == MotionEvent.ACTION_DOWN) {
  14. if (mMotionTarget != null) {
  15. // this is weird, we got a pen down, but we thought it was
  16. // already down!
  17. // XXX: We should probably send an ACTION_UP to the current
  18. // target.
  19. mMotionTarget = null;
  20. }
  21. // If we're disallowing intercept or if we're allowing and we didn't
  22. // intercept
  23. if (disallowIntercept || !onInterceptTouchEvent(ev)) {
  24. // reset this event's action (just to protect ourselves)
  25. ev.setAction(MotionEvent.ACTION_DOWN);
  26. // We know we want to dispatch the event down, find a child
  27. // who can handle it, start with the front-most child.
  28. final int scrolledXInt = (int) scrolledXFloat;
  29. final int scrolledYInt = (int) scrolledYFloat;
  30. final View[] children = mChildren;
  31. final int count = mChildrenCount;
  32. for (int i = count - 1; i >= 0; i--) {
  33. final View child = children[i];
  34. if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
  35. || child.getAnimation() != null) {
  36. child.getHitRect(frame);
  37. if (frame.contains(scrolledXInt, scrolledYInt)) {
  38. // offset the event to the view's coordinate system
  39. final float xc = scrolledXFloat - child.mLeft;
  40. final float yc = scrolledYFloat - child.mTop;
  41. ev.setLocation(xc, yc);
  42. child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
  43. if (child.dispatchTouchEvent(ev)) {
  44. // Event handled, we have a target now.
  45. mMotionTarget = child;
  46. return true;
  47. }
  48. // The event didn't get handled, try the next view.
  49. // Don't reset the event's location, it's not
  50. // necessary here.
  51. }
  52. }
  53. }
  54. }
  55. } ....//other code omitted
16行:进入ACTION_DOWN的处理
17-23行:将mMotionTarget置为null
26行:进行判断: if(disallowIntercept || !onInterceptTouchEvent(ev))
    两种可能会进入IF代码段
    1、当前不允许拦截,即disallowIntercept =true,
    2、当前允许拦截但是未进行拦截,即disallowIntercept =false,但是onInterceptTouchEvent(ev)返回false ;
    注:disallowIntercept 可以通过viewGroup.requestDisallowInterceptTouchEvent(boolean)进行设置;而onInterceptTouchEvent(ev)可以进行复写。
36-57行:开始遍历所有的子View
41行:进行判断当前的x,y坐标是否落在子View身上,如果在47行执行child.dispatchTouchEvent(ev),就进入了View的dispatchTouchEvent代码中了,当child.dispatchTouchEvent(ev)返回true,则为mMotionTarget=child;然后return true;
ViewGroup的ACTION_DOWN分析结束,总结一下:
ViewGroup实现捕获到DOWN事件,如果代码中不做TOUCH事件拦截,则开始查找当前x,y是否在某个子View的区域内,如果在,则把事件分发下去。

2.ACTION_MOVE

   
   
  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent ev) {
  3. final int action = ev.getAction();
  4. final float xf = ev.getX();
  5. final float yf = ev.getY();
  6. final float scrolledXFloat = xf + mScrollX;
  7. final float scrolledYFloat = yf + mScrollY;
  8. final Rect frame = mTempRect;
  9. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  10. //...ACTION_DOWN
  11. //...ACTIN_UP or ACTION_CANCEL
  12. // The event wasn't an ACTION_DOWN, dispatch it to our target if
  13. // we have one.
  14. final View target = mMotionTarget;
  15. // if have a target, see if we're allowed to and want to intercept its
  16. // events
  17. if (!disallowIntercept && onInterceptTouchEvent(ev)) {
  18. //....
  19. }
  20. // finally offset the event to the target's coordinate system and
  21. // dispatch the event.
  22. final float xc = scrolledXFloat - (float) target.mLeft;
  23. final float yc = scrolledYFloat - (float) target.mTop;
  24. ev.setLocation(xc, yc);
  25. return target.dispatchTouchEvent(ev);
  26. }
18行:把ACTION_DOWN时赋值的mMotionTarget,付给target ;
23行:if (!disallowIntercept && onInterceptTouchEvent(ev)) 当前允许拦截且拦截了,才进入IF体,当然了默认是不会拦截的~这里执行了onInterceptTouchEvent(ev)
28-30行:把坐标系统转化为子View的坐标系统
32行:直接return target.dispatchTouchEvent(ev);
可以看到,正常流程下,ACTION_MOVE在检测完是否拦截以后,直接调用了子View.dispatchTouchEvent,事件分发下去;

3.ACTION_UP

   
   
  1. public boolean dispatchTouchEvent(MotionEvent ev) {
  2. if (!onFilterTouchEventForSecurity(ev)) {
  3. return false;
  4. }
  5. final int action = ev.getAction();
  6. final float xf = ev.getX();
  7. final float yf = ev.getY();
  8. final float scrolledXFloat = xf + mScrollX;
  9. final float scrolledYFloat = yf + mScrollY;
  10. final Rect frame = mTempRect;
  11. boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
  12. if (action == MotionEvent.ACTION_DOWN) {...}
  13. boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
  14. (action == MotionEvent.ACTION_CANCEL);
  15. if (isUpOrCancel) {
  16. mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
  17. }
  18. final View target = mMotionTarget;
  19. if(target ==null ){...}
  20. if (!disallowIntercept && onInterceptTouchEvent(ev)) {...}
  21. if (isUpOrCancel) {
  22. mMotionTarget = null;
  23. }
  24. // finally offset the event to the target's coordinate system and
  25. // dispatch the event.
  26. final float xc = scrolledXFloat - (float) target.mLeft;
  27. final float yc = scrolledYFloat - (float) target.mTop;
  28. ev.setLocation(xc, yc);
  29. return target.dispatchTouchEvent(ev);
  30. }
17行:判断当前是否是ACTION_UP
21,28行:分别重置拦截标志位以及将DOWN赋值的mMotionTarget置为null,都UP了,当然置为null,下一次DOWN还会再赋值的~
最后,修改坐标系统,然后调用target.dispatchTouchEvent(ev);

正常情况下,即我们上例整个代码的流程我们已经走完了:
1、ACTION_DOWN中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则找到包含当前x,y坐标的子View,赋值给mMotionTarget,然后调用    mMotionTarget.dispatchTouchEvent
2、ACTION_MOVE中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)
3、ACTION_UP中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)
当然了在分发之前都会修改下坐标系统,把当前的x,y分别减去child.left 和 child.top ,然后传给child;

问题1:如何在ViewGroup中进行事件拦截?
默认是不拦截的,即返回false;如果你需要拦截,只要在onInterceptTouchEvent()中return true就行了,该事件就不会往子View传递了,并且如果你在DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。
原因很简单,当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ; 

问题2:如果ViewGroup的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截了子View的MOVE以及UP事件;此时子View希望依然能够响应MOVE和UP时该咋办呢?
requestDisallowInterceptTouchEvent(boolean) 用于设置是否允许拦截,我们在子View的dispatchTouchEvent中直接通过getParent().requestDisallowInterceptTouchEvent(true)设置子view所在ViewGroup的disallowIntercept:
   
   
  1. @Override
  2. public boolean dispatchTouchEvent(MotionEvent event)
  3. {
  4. getParent().requestDisallowInterceptTouchEvent(true);
  5. int action = event.getAction();
  6. switch (action)
  7. {
  8. case MotionEvent.ACTION_DOWN:
  9. Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
  10. break;
  11. case MotionEvent.ACTION_MOVE:
  12. Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
  13. break;
  14. case MotionEvent.ACTION_UP:
  15. Log.e(TAG, "dispatchTouchEvent ACTION_UP");
  16. break;
  17. default:
  18. break;
  19. }
  20. return super.dispatchTouchEvent(event);
  21. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值