ViewGroup源码解读

xiaoxiao2021-02-28  71

  我们之前刚刚分析完事件传递机制和view的源码,如果没有看过的,建议看完View的事件拦截机制浅析以及View的事件源码解析。这次我们来分析下viewgroup的。

  可能有人会想,怎么又是源码分析,肯定又是一大通。其实没你想的那么复杂。仔细分析一波就行了。

  解读ViewGroup

  我们都知道,一个事件完整的流程是从dispatchTouchevent–>onInterceptTouchevent–>onTouchEvent。我们先不说事件监听的问题。上述三个步骤就是正常一个点击的流程。前面我们分析view的时候发现它并没有onInterceptTouchevent这个方法。这个我之前有提到,view已经是最底层了,所以就不需要拦截了。而这一整套的机制就是在ViewGroup中体现出来的。我们先来看一张图:

  触摸事件发生后,在Activity内最先接收到事件的是Activity自身的dispatchTouchEvent,然后Activity传递给Activity的Window。接着Window传递给最顶端的View,也就是DecorView。接下来才是我们熟悉的触摸事件流程:首先是最顶端的ViewGroup(这边便是DecorView)的dispatchTouchEvent接收到事件。并通过onInterceptTouchEvent判断是否需要拦截。如果拦截则分配到ViewGroup自身的onTouchEvent,如果不拦截则查找位于点击区域的子View(当事件是ACTION_DOWN的时候,会做一次查找并根据查找到的子View设定一个TouchTarget,有了TouchTarget以后,后续的对应id的事件如果不被拦截都会分发给这一个TouchTarget)。查找到子View以后则调用dispatchTransformedTouchEvent把MotionEvent的坐标转换到子View的坐标空间,这不仅仅是x,y的偏移,还包括根据子View自身矩阵的逆矩阵对坐标进行变换(这就是使用setTranslationX,setScaleX等方法调用后,子View的点击区域还能保持和自身绘制内容一致的原因。使用Animation做变换点击区域不同步是因为Animation使用的是Canvas的矩阵而不是View自身的矩阵来做变换)。

  dispatchTouchevent分析

  我们先放上dispatchTouchevent的源码,然后一步一步来分析:

  public boolean dispatchTouchEvent(MotionEvent ev) {

  if (mInputEventConsistencyVerifier != null) {

  mInputEventConsistencyVerifier.onTouchEvent(ev, 1);

  }

  // If the event targets the accessibility focused view and this is it, start

  // normal event dispatch. Maybe a descendant is what will handle the click.

  if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {

  ev.setTargetAccessibilityFocus(false);

  }

  boolean handled = false;

  if (onFilterTouchEventForSecurity(ev)) {

  final int action = ev.getAction();

  final int actionMasked = action & MotionEvent.ACTION_MASK;

  // Handle an initial down.

  if (actionMasked == MotionEvent.ACTION_DOWN) {

  // Throw away all previous state when starting a new touch gesture.

  // The framework may have dropped the up or cancel event for the previous gesture

  // due to an app switch, ANR, or some other state change.

  cancelAndClearTouchTargets(ev);

  resetTouchState();

  }

  // Check for interception.

  final boolean intercepted;

  if (actionMasked == MotionEvent.ACTION_DOWN

  || mFirstTouchTarget != null) {

  final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

  if (!disallowIntercept) {

  intercepted = onInterceptTouchEvent(ev);

  ev.setAction(action); // restore action in case it was changed

  } else {

  intercepted = false;

  }

  } else {

  // There are no touch targets and this action is not an initial down

  // so this view group continues to intercept touches.

  intercepted = true;

  }

  // If intercepted, start normal event dispatch. Also if there is already

  // a view that is handling the gesture, do normal event dispatch.

  if (intercepted || mFirstTouchTarget != null) {

  ev.setTargetAccessibilityFocus(false);

  }

  // Check for cancelation.

  final boolean canceled = resetCancelNextUpFlag(this)

  || actionMasked == MotionEvent.ACTION_CANCEL;

  // Update list of touch targets for pointer down, if needed.

  final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;

  TouchTarget newTouchTarget = null;

  boolean alreadyDispatchedToNewTouchTarget = false;

  if (!canceled && !intercepted) {

  // If the event is targeting accessiiblity focus we give it to the

  // view that has accessibility focus and if it does not handle it

  // we clear the flag and dispatch the event to all children as usual.

  // We are looking up the accessibility focused host to avoid keeping

  // state since these events are very rare.

  View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()

  ? findChildWithAccessibilityFocus() : null;

  if (actionMasked == MotionEvent.ACTION_DOWN

  || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)

  || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

  final int actionIndex = ev.getActionIndex(); // always 0 for down

  final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)

  : TouchTarget.ALL_POINTER_IDS;

  // Clean up earlier touch targets for this pointer id in case they

  // have become out of sync.

  removePointersFromTouchTargets(idBitsToAssign);

  final int childrenCount = mChildrenCount;

  if (newTouchTarget == null && childrenCount != 0) {

  final float x = ev.getX(actionIndex);

  final float y = ev.getY(actionIndex);

  // Find a child that can receive the event.

  // Scan children from front to back.

  final ArrayList preorderedList = buildTouchDispatchChildList();

  final boolean customOrder = preorderedList == null

  && isChildrenDrawingOrderEnabled();

  final View[] children = mChildren;

  for (int i = childrenCount - 1; i >= 0; i--) {

  final int childIndex = getAndVerifyPreorderedIndex(

  childrenCount, i, customOrder);

  final View child = getAndVerifyPreorderedView(

  preorderedList, children, childIndex);

  // If there is a view that has accessibility focus we want it

  // to get the event first and if not handled we will perform a

  // normal dispatch. We may do a double iteration but this is

  // safer given the timeframe.

  if (childWithAccessibilityFocus != null) {

  if (childWithAccessibilityFocus != child) {

  continue;

  }

  childWithAccessibilityFocus = null;

  i = childrenCount - 1;

  }

  if (!canViewReceivePointerEvents(child)

  || !isTransformedTouchPointInView(x, y, child, null)) {

  ev.setTargetAccessibilityFocus(false);

  continue;

  }

  newTouchTarget = getTouchTarget(child);

  if (newTouchTarget != null) {

  // Child is already receiving touch within its bounds.

  // Give it the new pointer in addition to the ones it is handling.

  newTouchTarget.pointerIdBits |= idBitsToAssign;

  break;

  }

  resetCancelNextUpFlag(child);

  if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

  // Child wants to receive touch within its bounds.

  mLastTouchDownTime = ev.getDownTime();

  if (preorderedList != null) {

  // childIndex points into presorted list, find original index

  for (int j = 0; j < childrenCount; j++) {

  if (children[childIndex] == mChildren[j]) {

  mLastTouchDownIndex = j;

  break;

  }

  }

  } else {

  mLastTouchDownIndex = childIndex;

  }

  mLastTouchDownX = ev.getX();

  mLastTouchDownY = ev.getY();

  newTouchTarget = addTouchTarget(child, idBitsToAssign);

  alreadyDispatchedToNewTouchTarget = true;

  break;

  }

  // The accessibility focus didn't handle the event, so clear

  // the flag and do a normal dispatch to all children.

  ev.setTargetAccessibilityFocus(false);

  }

  if (preorderedList != null) preorderedList.clear();

  }

  if (newTouchTarget == null && mFirstTouchTarget != null) {

  // Did not find a child to receive the event.

  // Assign the pointer to the least recently added target.

  newTouchTarget = mFirstTouchTarget;

  while (newTouchTarget.next != null) {

  newTouchTarget = newTouchTarget.next;

  }

  newTouchTarget.pointerIdBits |= idBitsToAssign;

  }

  }

  }

  // Dispatch to touch targets.

  if (mFirstTouchTarget == null) {

  // No touch targets so treat this as an ordinary view.

  handled = dispatchTransformedTouchEvent(ev, canceled, null,

  TouchTarget.ALL_POINTER_IDS);

  } else {

  // Dispatch to touch targets, excluding the new touch target if we already

  // dispatched to it. Cancel touch targets if necessary.

  TouchTarget predecessor = null;

  TouchTarget target = mFirstTouchTarget;

  while (target != null) {

  final TouchTarget next = target.next;

  if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {

  handled = true;

  } else {

  final boolean cancelChild = resetCancelNextUpFlag(target.child)

  || intercepted;

  if (dispatchTransformedTouchEvent(ev, cancelChild,

  target.child, target.pointerIdBits)) {

  handled = true;

  }

  if (cancelChild) {

  if (predecessor == null) {

  mFirstTouchTarget = next;

  } else {

  predecessor.next = next;

  }

  target.recycle();

  target = next;

  continue;

  }

  }

  predecessor = target;

  target = next;

  }

  }

  // Update list of touch targets for pointer up or cancel, if needed.

  if (canceled

  || actionMasked == MotionEvent.ACTION_UP

  || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

  resetTouchState();

  } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {

  final int actionIndex = ev.getActionIndex();

  final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);

  removePointersFromTouchTargets(idBitsToRemove);

  }

  }

  if (!handled && mInputEventConsistencyVerifier != null) {

  mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);

  }

  return handled;

  }

  123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195

  是不是整个人都蒙蔽了,这么长一串。其实整段代码可以缩减成几句话,就是这样:

  public boolean dispatchTouchEvent(MotionEvent ev) {

  boolean result = false;

  if (!onInterceptTouchEvent(ev)) {

  result = child.dispatchTouchEvent(ev);

  }

  if (!result) {

  result = onTouchEvent(ev);

  }

  return result;

  }

宁波最好的整形医院http://www.lyxlc.org

转载请注明原文地址: https://www.6miu.com/read-51066.html

最新回复(0)