CoordinatorLayout和Behavior(三)

438 阅读3分钟
Behavior与Measure、Layout

Behavior提供了以下两个方法来代理CoordinatorLayout的onMeasureChild和onLayoutChild。

// child是被Measure的子View。
// widthUsed和heightUsed是CoordinatorLayout已使用的宽高
// 返回true则Behavior代理CoordinatorLayout的子View测量,否则由CoordinatorLayout负责。默认是返回false。
public boolean onMeasureChild(CoordinatorLayout parent, V child,
  int parentWidthMeasureSpec, int widthUsed,
  int parentHeightMeasureSpec, int heightUsed) {
  return false;
}
// child是被Layout的子View。
// 返回true则Behavior代理CoordinatorLayout的子View布局,否则由CoordinatorLayout负责。默认是返回false。
// 注意1:被依赖的View总是优先于依赖的View被Layout,不管View的顺序如何。
// 注意2:如果Behavior重写了onDependentViewChanged方法,那么它也该重写onLayoutChild方法。
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
  return false;
}

看看Coordinator是如何回调Behavior的这两个方法。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // 把子View根据依赖关系进行排序,被依赖的排在前面
  prepareChildren();
  // 注册ViewTreeObserver.OnPreDrawListener监听器
  ensurePreDrawListener();

  final int paddingLeft = getPaddingLeft();
  final int paddingTop = getPaddingTop();
  final int paddingRight = getPaddingRight();
  final int paddingBottom = getPaddingBottom();
  final int layoutDirection = ViewCompat.getLayoutDirection(this);
  final boolean isRtl = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL;
  final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

  final int widthPadding = paddingLeft + paddingRight;
  final int heightPadding = paddingTop + paddingBottom;
  int widthUsed = getSuggestedMinimumWidth();
  int heightUsed = getSuggestedMinimumHeight();
  int childState = 0;

  final boolean applyInsets = mLastInsets != null && ViewCompat.getFitsSystemWindows(this);

  final int childCount = mDependencySortedChildren.size();
  // 遍历每个子View进行测量
  for (int i = 0; i < childCount; i++) {
    final View child = mDependencySortedChildren.get(i);
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();

    // 计算keyline使用的距离
    int keylineWidthUsed = 0;
    if (lp.keyline >= 0 && widthMode != MeasureSpec.UNSPECIFIED) {
      final int keylinePos = getKeyline(lp.keyline);
      final int keylineGravity = GravityCompat.getAbsoluteGravity(
              resolveKeylineGravity(lp.gravity), layoutDirection)
              & Gravity.HORIZONTAL_GRAVITY_MASK;
      if ((keylineGravity == Gravity.LEFT && !isRtl)
              || (keylineGravity == Gravity.RIGHT && isRtl)) {
        keylineWidthUsed = Math.max(0, widthSize - paddingRight - keylinePos);
      } else if ((keylineGravity == Gravity.RIGHT && !isRtl)
              || (keylineGravity == Gravity.LEFT && isRtl)) {
        keylineWidthUsed = Math.max(0, keylinePos - paddingLeft);
      }
    }

    int childWidthMeasureSpec = widthMeasureSpec;
    int childHeightMeasureSpec = heightMeasureSpec;
    // 如果调用了setWindowInsets方法,计算Inset占用的宽高。
    if (applyInsets && !ViewCompat.getFitsSystemWindows(child)) {
      final int horizInsets = mLastInsets.getSystemWindowInsetLeft()
              + mLastInsets.getSystemWindowInsetRight();
      final int vertInsets = mLastInsets.getSystemWindowInsetTop()
              + mLastInsets.getSystemWindowInsetBottom();
      // 减去Inset占用的宽高。
      childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
              widthSize - horizInsets, widthMode);
      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
              heightSize - vertInsets, heightMode);
    }

    final Behavior b = lp.getBehavior();
    // 如果Behavior不为空且Behavior不处理子View的测量,才调用CoordinatorLayout的onMeasureChild方法。
    if (b == null || !b.onMeasureChild(this, child, childWidthMeasureSpec, keylineWidthUsed,
            childHeightMeasureSpec, 0)) {
      onMeasureChild(child, childWidthMeasureSpec, keylineWidthUsed,
              childHeightMeasureSpec, 0);
    }

    widthUsed = Math.max(widthUsed, widthPadding + child.getMeasuredWidth() +
            lp.leftMargin + lp.rightMargin);

    heightUsed = Math.max(heightUsed, heightPadding + child.getMeasuredHeight() +
            lp.topMargin + lp.bottomMargin);
    childState = ViewCompat.combineMeasuredStates(childState,
            ViewCompat.getMeasuredState(child));
  }

  final int width = ViewCompat.resolveSizeAndState(widthUsed, widthMeasureSpec,
          childState & ViewCompat.MEASURED_STATE_MASK);
  final int height = ViewCompat.resolveSizeAndState(heightUsed, heightMeasureSpec,
          childState << ViewCompat.MEASURED_HEIGHT_STATE_SHIFT);
  setMeasuredDimension(width, height);
}

void ensurePreDrawListener() {
  boolean hasDependencies = false;
  final int childCount = getChildCount();
  // 遍历所有子View,检查是否有依赖关系
  for (int i = 0; i < childCount; i++) {
    final View child = getChildAt(i);
    if (hasDependencies(child)) {
      hasDependencies = true;
      break;
    }
  }
  // 如果有,则添加OnPreDrawListener监听器。
  // 如果没有,则移除OnPreDrawListener。
  if (hasDependencies != mNeedsPreDrawListener) {
    if (hasDependencies) {
      addPreDrawListener();
    } else {
      removePreDrawListener();
    }
  }
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
  final int layoutDirection = ViewCompat.getLayoutDirection(this);
  final int childCount = mDependencySortedChildren.size();
  for (int i = 0; i < childCount; i++) {
    final View child = mDependencySortedChildren.get(i);
    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
    final Behavior behavior = lp.getBehavior();
    // 如果Behavior不为空且Behavior不处理子View的不仅,才调用CoordinatorLayout的onLayoutChild方法。
    if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
      onLayoutChild(child, layoutDirection);
    }
  }
}

public void onLayoutChild(View child, int layoutDirection) {
  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
  if (lp.checkAnchorChanged()) {
    // 在布局完成之前,不能改变子View的锚点。
    throw new IllegalStateException("An anchor may not be changed after CoordinatorLayout"
            + " measurement begins before layout is complete.");
  }
  if (lp.mAnchorView != null) {
    // 如果有锚点,需要根据锚点位置进行布局
    layoutChildWithAnchor(child, lp.mAnchorView, layoutDirection);
  } else if (lp.keyline >= 0) {
    // 根据keyline进行布局
    layoutChildWithKeyline(child, lp.keyline, layoutDirection);
  } else {
    layoutChild(child, layoutDirection);
  }
}   

// 根据锚点位置进行布局
private void layoutChildWithAnchor(View child, View anchor, int layoutDirection) {
  final LayoutParams lp = (LayoutParams) child.getLayoutParams();

  final Rect anchorRect = mTempRect1;
  final Rect childRect = mTempRect2;
  // 计算锚点View的位置
  getDescendantRect(anchor, anchorRect); 
  // 根据锚点计算子View的位置
  getDesiredAnchoredChildRect(child, layoutDirection, anchorRect, childRect);

  child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom);
}