龙盟编程博客 | 无障碍搜索 | 云盘搜索神器
快速搜索
主页 > 移动开发 > Android开发 >

Android Touch事件分发过程详解(3)

时间:2014-09-12 02:14来源:网络整理 作者:网络 点击:
分享到:
输出: 08-31 03:40:17.036: D/(1688): ### Activiti中getWindow()获取的类型是 : com.android.internal.policy.impl.PhoneWindow@5287fe38 OK,废话不多说,我们还是继续看PhoneWindow中的

输出:

08-31 03:40:17.036: D/(1688): ### Activiti中getWindow()获取的类型是 : com.android.internal.policy.impl.PhoneWindow@5287fe38 

OK,废话不多说,我们还是继续看PhoneWindow中的superDispatchTouchEvent函数吧。

@Override 
public boolean superDispatchTouchEvent(MotionEvent event) { 
  return mDecor.superDispatchTouchEvent(event); 
} 

恩,调用的是mDecor的superDispatchTouchEvent(event)函数,这个mDecor就是我们上面所说的DecorView类型,也就是我们看到的Activity上的所有内容的一个顶层ViewGroup,即整个ViewTree的根节点。看看它的声明吧。

// This is the top-level view of the window, containing the window decor. 
private DecorView mDecor; 

DecorView

那么我继续看看DecorView到底是个什么玩意儿吧。

  private final class DecorView extends FrameLayout implements RootViewSurfaceTaker { 
    /* package */int mDefaultOpacity = PixelFormat.OPAQUE; 
 
    /** The feature ID of the panel, or -1 if this is the application's DecorView */ 
    private final int mFeatureId; 
 
    private final Rect mDrawingBounds = new Rect(); 
 
    private final Rect mBackgroundPadding = new Rect(); 
 
    private final Rect mFramePadding = new Rect(); 
 
    private final Rect mFrameOffsets = new Rect(); 
 
    private boolean mChanging; 
 
    private Drawable mMenuBackground; 
    private boolean mWatchingForMenu; 
    private int mDownY; 
 
    public DecorView(Context context, int featureId) { 
      super(context); 
      mFeatureId = featureId; 
    } 
 
    @Override 
    public boolean dispatchKeyEvent(KeyEvent event) { 
      final int keyCode = event.getKeyCode(); 
      // 代码省略 
      return isDown ? PhoneWindow.this.onKeyDown(mFeatureId, event.getKeyCode(), event) 
          : PhoneWindow.this.onKeyUp(mFeatureId, event.getKeyCode(), event); 
    } 
 
    @Override 
    public boolean dispatchTouchEvent(MotionEvent ev) { 
      final Callback cb = getCallback(); 
      return cb != null && mFeatureId < 0 ? cb.dispatchTouchEvent(ev) : super 
          .dispatchTouchEvent(ev); 
    } 
 
    @Override 
    public boolean dispatchTrackballEvent(MotionEvent ev) { 
      final Callback cb = getCallback(); 
      return cb != null && mFeatureId < 0 ? cb.dispatchTrackballEvent(ev) : super 
          .dispatchTrackballEvent(ev); 
    } 
 
    public boolean superDispatchKeyEvent(KeyEvent event) { 
      return super.dispatchKeyEvent(event); 
    } 
 
    public boolean superDispatchTouchEvent(MotionEvent event) { 
      return super.dispatchTouchEvent(event); 
    } 
 
    public boolean superDispatchTrackballEvent(MotionEvent event) { 
      return super.dispatchTrackballEvent(event); 
    } 
 
    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
      return onInterceptTouchEvent(event); 
    } 
// 代码省略 
} 

可以看到,DecorView继承自FrameLayout, 它对于touch事件的分发( dispatchTouchEvent )、处理都是交给super类来处理,也就是FrameLayout来处理,我们在FrameLayout中没有看到相应的实现,那继续跟踪到FrameLayout的父类,即ViewGroup,我们看到了dispatchTouchEvent的实现,那我们就先看ViewGroup (Android 2.3 源码)是如何进行事件分发的吧。

ViewGroup的Touch事件分发

/** 
 * {@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 
    // intercept 
    if (disallowIntercept || !onInterceptTouchEvent(ev))     // 1、是否禁用拦截、是否拦截事件 
      // 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--)    // 2、迭代所有子view,查找触摸事件在哪个子view的坐标范围内 
        final View child = children[i]; 
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE 
            || child.getAnimation() != null) { 
          child.getHitRect(frame);        // 3、获取child的坐标范围 
          if (frame.contains(scrolledXInt, scrolledYInt))  // 4、判断发生该事件坐标是否在该child坐标范围内 
            // 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))   // 5、child处理该事件 
              // 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; 
  } 
 
  return target.dispatchTouchEvent(ev); 
} 

精彩图集

赞助商链接