content: package com.jennifer.andy.nestedscrollingdemo.ui.cdl.behavior;
  
  import android.content.Context;
  import android.graphics.Rect;
  import android.support.design.widget.CoordinatorLayout;
  import android.support.v4.view.GravityCompat;
  import android.support.v4.view.ViewCompat;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.TextView;
  
  import java.util.List;
  
  /**
   * Author:  andy.xwt
   * Date:    2019-07-27 20:06
   * Description:该Behavior只负责测量与布局基本与{@link HeaderScrollingViewBehavior}非常类似。
   * 这里为了帮助大家理解如何理解在如何在CoordinatorLayout下，通过Behavior来控制控件的位置与高度
   */
  
  public class MeasureLayoutBehavior extends CoordinatorLayout.Behavior<View> {
  
  
      final Rect mTempRect1 = new Rect();
      final Rect mTempRect2 = new Rect();
  
      public static final String TAG = "MeasureLayoutBehavior";
  
      public MeasureLayoutBehavior() {
      }
  
      public MeasureLayoutBehavior(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
  
      /**
       * 依赖TextView
       */
      @Override
      public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
          return dependency instanceof TextView;
      }
  
  
      @Override
      public boolean onMeasureChild(CoordinatorLayout parent, View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
  
          //获取当前滚动控件的测量模式
          final int childLpHeight = child.getLayoutParams().height;
  
          //只有当前滚动控件为match_parent/wrap_content时才重新测量其高度，因为固定高度不会出现底部空白的情况
          if (childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
                  || childLpHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
  
              //获取当前child依赖的对象集合
              final List<View> dependencies = parent.getDependencies(child);
  
              final View header = findFirstDependency(dependencies);
              if (header != null) {
                  if (ViewCompat.getFitsSystemWindows(header)
                          && !ViewCompat.getFitsSystemWindows(child)) {
                      // If the header is fitting system windows then we need to also,
                      // otherwise we'll get CoL's compatible measuring
                      ViewCompat.setFitsSystemWindows(child, true);
  
                      if (ViewCompat.getFitsSystemWindows(child)) {
                          // If the set succeeded, trigger a new layout and return true
                          child.requestLayout();
                          return true;
                      }
                  }
                  //获取当前父控件中可用的距离，
                  int availableHeight = View.MeasureSpec.getSize(parentHeightMeasureSpec);
                  if (availableHeight == 0) {
  
                      // If the measure spec doesn't specify a size, use the current height
                      availableHeight = parent.getHeight();
                  }
                  //计算当前滚动控件的高度。
                  final int height = availableHeight - header.getMeasuredHeight() + getScrollRange(header);
                  final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height,
                          childLpHeight == ViewGroup.LayoutParams.MATCH_PARENT
                                  ? View.MeasureSpec.EXACTLY
                                  : View.MeasureSpec.AT_MOST);
  
                  //测量当前滚动的View的正确高度
                  parent.onMeasureChild(child, parentWidthMeasureSpec,
                          widthUsed, heightMeasureSpec, heightUsed);
  
                  return true;
              }
          }
          return false;
      }
  
      @Override
      public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
          final List<View> dependencies = parent.getDependencies(child);
          final View header = findFirstDependency(dependencies);
  
          if (header != null) {
              final CoordinatorLayout.LayoutParams lp =
                      (CoordinatorLayout.LayoutParams) child.getLayoutParams();
              final Rect available = mTempRect1;
  
              //设置当前的宽高 为当前header的下方
              available.set(parent.getPaddingLeft() + lp.leftMargin,
                      header.getBottom() + lp.topMargin,
                      parent.getWidth() - parent.getPaddingRight() - lp.rightMargin,
                      parent.getHeight() + header.getBottom()
                              - parent.getPaddingBottom() - lp.bottomMargin);
  
              //根据gravity重新计算坐标
              final Rect out = mTempRect2;
              GravityCompat.apply(resolveGravity(lp.gravity), child.getMeasuredWidth(),
                      child.getMeasuredHeight(), available, out, layoutDirection);
  
              //拿到坐标后重新布局
              child.layout(out.left, out.top, out.right, out.bottom);
  
          } else {
              //如果没有依赖，则调用父控件来处理布局
              parent.onLayoutChild(child, layoutDirection);
          }
          return true;
      }
  
  
      /**
       * 从依赖集合中获取第一个
       */
      View findFirstDependency(List<View> views) {
          if (views != null && !views.isEmpty()) {
              return views.get(0);
          }
          return null;
      }
  
      /**
       * 矫正当前Gravity
       */
      private static int resolveGravity(int gravity) {
          return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
      }
  
  
      /**
       * 获取当前View的滑动范围，一般情况下，为view的高度
       *
       * @param v
       * @return
       */
      int getScrollRange(View v) {
          return v.getMeasuredHeight();
      }
  }
  