RelativeLayout源码详解

人生如逆旅,我亦是行人.

RelativeLayout布局我们平时使用频率很高,但是由于平时业务繁忙和懒一直没有仔细阅读其内部实现机制,最近接到的需求逼得我不得不去仔细阅读它!让我们一起来看看它是如何实现的吧~

RelativeLayout源码详解_第1张图片
RelativeLayout流程图

onMeasure处理过程

  • 1.将子View根据横向和纵向关系进行排序
//先把子View根据纵向关系和横向关系排序
if (mDirtyHierarchy) {
  mDirtyHierarchy = false;
  sortChildren();
}

/**
 * 每次调用requestLayout都会被赋值为true
 */
@Override
public void requestLayout() {
  super.requestLayout();
  mDirtyHierarchy = true;
}

 /**
  * 排序子View函数
  */
private void sortChildren() {
  final int count = getChildCount();
  if (mSortedVerticalChildren == null || mSortedVerticalChildren.length != count) {
    mSortedVerticalChildren = new View[count];
  }

  if (mSortedHorizontalChildren == null || mSortedHorizontalChildren.length != count) {
    mSortedHorizontalChildren = new View[count];
  }

  final DependencyGraph graph = mGraph;
  graph.clear();

  for (int i = 0; i < count; i++) {
    graph.add(getChildAt(i));
  }

  //根据垂直方向的规则去排序垂直方向的子view
  graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
  //根据水平方向的规则去排序水平方向的子view
  graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
}

依赖图DependencyGraph类和Node节点

private static class DependencyGraph {
    /**
     * List of all views in the graph.
     * 列出所有的View放在图中
     */
    private ArrayList mNodes = new ArrayList();

    /**
     * List of nodes in the graph. Each node is identified by its
     * view id (see View#getId()).
     * 图中的所有节点都会有一个特定的id
     */
    private SparseArray mKeyNodes = new SparseArray();

    /**
     * Temporary data structure used to build the list of roots
     * for this graph.
     * 

* 临时数据结构用于构建此图的根列表。 */ private ArrayDeque mRoots = new ArrayDeque(); /** * Clears the graph. */ void clear() { final ArrayList nodes = mNodes; final int count = nodes.size(); for (int i = 0; i < count; i++) { nodes.get(i).release(); } nodes.clear(); mKeyNodes.clear(); mRoots.clear(); } /** * Adds a view to the graph. * 将view添加进图中 * * @param view The view to be added as a node to the graph. */ void add(View view) { final int id = view.getId(); //有图就有节点,根据view生成一个节点 final Node node = Node.acquire(view); //如果当前的view有有效id则将其加入List中 if (id != View.NO_ID) { mKeyNodes.put(id, node); } mNodes.add(node); } /** * Builds a sorted list of views. The sorting order depends on the dependencies * between the view. For instance, if view C needs view A to be processed first * and view A needs view B to be processed first, the dependency graph * is: B -> A -> C. The sorted array will contain views B, A and C in this order. *

* 创建一个有序的view列表 * * @param sorted The sorted list of views. The length of this array must * be equal to getChildCount(). * @param rules The list of rules to take into account. */ void getSortedViews(View[] sorted, int... rules) { //首先找到不依赖别的view的view作为root节点 final ArrayDeque roots = findRoots(rules); int index = 0; Node node; //读取roots下一个node,直到全部遍历完 while ((node = roots.pollLast()) != null) { //取得view final View view = node.view; //取得view对应的id值 final int key = view.getId(); //把符合规则的view加入到sorted中 sorted[index++] = view; //根据findRoots()方法分析 //dependents里存的是依赖别人的node //如果A、C依赖的是B, 那么B的依赖表中存的是A、C final ArrayMap dependents = node.dependents; final int count = dependents.size(); //编辑所有依赖自己的node for (int i = 0; i < count; i++) { final Node dependent = dependents.keyAt(i); //dependencies存的是被依赖node的规则和node //如果A依赖B和D才能确定位置,那么dependcies = B,D final SparseArray dependencies = dependent.dependencies; //移除当前node和dependencies的依赖关系 //如果dependencies只和当前的node有依赖,那么移除之后 //dependencies node也视为rootNode //如果B只被A依赖,那么移除和A的关系之后,B就不被任何Node依赖 dependencies.remove(key); if (dependencies.size() == 0) { roots.add(dependent); } } } //循环依赖异常报错 if (index < sorted.length) { throw new IllegalStateException("Circular dependencies cannot exist" + " in RelativeLayout"); } } /** * Finds the roots of the graph. A root is a node with no dependency and * with [0..n] dependents. * * @param rulesFilter The list of rules to consider when building the * dependencies * @return A list of node, each being a root of the graph */ private ArrayDeque findRoots(int[] rulesFilter) { final SparseArray keyNodes = mKeyNodes; final ArrayList nodes = mNodes; final int count = nodes.size(); // Find roots can be invoked several times, so make sure to clear // all dependents and dependencies before running the algorithm //查找根节点可能或被调用多次,因此要在运行算法之前确保清除所有依赖关系 for (int i = 0; i < count; i++) { final Node node = nodes.get(i); node.dependents.clear(); node.dependencies.clear(); } // Builds up the dependents and dependencies for each node of the graph // 构建图的每个节点的依赖关系和依赖 // 遍历所有node,node里存了当前的view,它所依赖的关系 for (int i = 0; i < count; i++) { final Node node = nodes.get(i); final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams(); //取出当前view所有的依赖关系 final int[] rules = layoutParams.mRules; final int rulesCount = rulesFilter.length; // Look only the the rules passed in parameter, this way we build only the // dependencies for a specific set of rules //只查看参数中传递的规则,这样我们只构建一条特殊规则的依赖关系 for (int j = 0; j < rulesCount; j++) { final int rule = rules[rulesFilter[j]]; if (rule > 0) { // The node this node depends on // 该节点依赖的节点 final Node dependency = keyNodes.get(rule); // Skip unknowns and self dependencies // 忽略未知情况和自我依赖 if (dependency == null || dependency == node) { continue; } // 这里一定要分清楚dependencies和dependency //dependents里存的是依赖别人的node //dependencies存的是被依赖node的规则和node //举个例子 A View toLeftOf B View // A 依赖 B //dependecy.dependents.put(A,this); //node.dependencies.put(rule,B); // Add the current node as a dependent dependency.dependents.put(node, this); // Add a dependency to the current node node.dependencies.put(rule, dependency); } } } final ArrayDeque roots = mRoots; roots.clear(); // Finds all the roots in the graph: all nodes with no dependencies // 找到图中所有的根节点:所有节点无依赖 for (int i = 0; i < count; i++) { final Node node = nodes.get(i); //如果node里存的依赖关系为0,即该view不依赖任何view if (node.dependencies.size() == 0) roots.addLast(node); } return roots; } /** * A node in the dependency graph. A node is a view, its list of dependencies * and its list of dependents. *

* 依赖图中的一个节点。 节点是一个View,其依赖关系列表及其依赖列表。 *

* A node with no dependent is considered a root of the graph. * 一个节点没有依赖的话会被认为是这个视图根节点 */ static class Node { /** * The view representing this node in the layout. * 在布局中表示此节点的视图。 */ View view; /** * The list of dependents for this node; a dependent is a node * that needs this node to be processed first. * * 此节点的依赖列表; 一个依赖者是一个需要首先处理该节点的节点。 */ final ArrayMap dependents = new ArrayMap(); /** * The list of dependencies for this node. * * 依赖该节点的列表。 */ final SparseArray dependencies = new SparseArray(); /* * START POOL IMPLEMENTATION */ // The pool is static, so all nodes instances are shared across // activities, that's why we give it a rather high limit private static final int POOL_LIMIT = 100; private static final SynchronizedPool sPool = new SynchronizedPool(POOL_LIMIT); static Node acquire(View view) { Node node = sPool.acquire(); if (node == null) { node = new Node(); } node.view = view; return node; } void release() { view = null; dependents.clear(); dependencies.clear(); sPool.release(this); } /* * END POOL IMPLEMENTATION */ } }

这里的dependents和dependencies有点绕,需要好好理解....

  • 2.相关变量的初始化赋值操作
    int myWidth = -1;
    int myHeight = -1;

    int width = 0;
    int height = 0;

    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    final int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    // Record our dimensions if they are known;

    // 根据MeasureSpec去确定尺寸

    if (widthMode != MeasureSpec.UNSPECIFIED) {
        myWidth = widthSize;
    }

    if (heightMode != MeasureSpec.UNSPECIFIED) {
        myHeight = heightSize;
    }

    if (widthMode == MeasureSpec.EXACTLY) {
        width = myWidth;
    }

    if (heightMode == MeasureSpec.EXACTLY) {
        height = myHeight;
    }

    View ignore = null;
    //判断是否是Gravity.START和Gravity.TOP
    //目的是确定左上角的坐标
    int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
    final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
    gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
    final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;

    int left = Integer.MAX_VALUE;
    int top = Integer.MAX_VALUE;
    int right = Integer.MIN_VALUE;
    int bottom = Integer.MIN_VALUE;

    boolean offsetHorizontalAxis = false;
    boolean offsetVerticalAxis = false;

    //记录ignore的view
    if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
        ignore = findViewById(mIgnoreGravity);
    }

    //宽度和高度是否是wrap模式
    final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
    final boolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;

    // We need to know our size for doing the correct computation of children positioning in RTL
    // mode but there is no practical way to get it instead of running the code below.
    // So, instead of running the code twice, we just set the width to a "default display width"
    // before the computation and then, as a last pass, we will update their real position with
    // an offset equals to "DEFAULT_WIDTH - width".

    // 我们需要知道我们的大小是为了正确计算在RTL模式下的孩子定位,但没有实际的方法来获取它,而不是运行下面的代码。
    // 因此,我们只是在计算之前将宽度设置为“默认显示宽度”,而不是运行代码,而是作为最后一次通过,
    // 将以“DEFAULT_WIDTH - width”的偏移量更新其实际位置。

    final int layoutDirection = getLayoutDirection();
    if (isLayoutRtl() && myWidth == -1) {
        myWidth = DEFAULT_WIDTH;
    }
  • 3.处理水平方向的子View
//水平子View的集合
View[] views = mSortedHorizontalChildren;
int count = views.length;

for (int i = 0; i < count; i++) {
  View child = views[i];
  if (child.getVisibility() != GONE) {
    LayoutParams params = (LayoutParams) child.getLayoutParams();
    //根据方向获得子view中设置的规则
    int[] rules = params.getRules(layoutDirection);
    //根据这些左右方向的规则转化成左右坐标
    applyHorizontalSizeRules(params, myWidth, rules);
    //测量水平方向子view的尺寸
    measureChildHorizontal(child, params, myWidth, myHeight);
    //确定水平方向子view位置
    if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
      offsetHorizontalAxis = true;
    }
  }
}

private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth, int[] rules) {
  RelativeLayout.LayoutParams anchorParams;

  // VALUE_NOT_SET indicates a "soft requirement" in that direction. For example:
  // left=10, right=VALUE_NOT_SET means the view must start at 10, but can go as far as it
  // wants to the right
  // left=VALUE_NOT_SET, right=10 means the view must end at 10, but can go as far as it
  // wants to the left
  // left=10, right=20 means the left and right ends are both fixed

  // VALUE_NOT_SET 表示的是在方向上的"软需求"
  //例如: left = 10, right = VALUE_NOT_SET 意味着这个view必须在10的位置的开始,但是可以向右移动
  //     left=VALUE_NOT_SET, right=10 意味着这个view必须在10的位置结束,但是可以向左右移动
  //     left=10, right=20 意味着这个view的左右已经被固定

  childParams.mLeft = VALUE_NOT_SET;
  childParams.mRight = VALUE_NOT_SET;

  //取得的当前子View的LEFT_OF属性对应的View
  anchorParams = getRelatedViewParams(rules, LEFT_OF);
  if (anchorParams != null) {
    //如果设置了这个属性,那么当前子View的右坐标就是layout_toLeftOf属性对应的View的左边坐标减去对应View的左右Margin值
    childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                                               childParams.rightMargin);
  } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
    //如果alignWithParent == true 并且也 LEFT_OF属性存在
    //alignWithParent的值对应的是alignWithParentIfMissing
    //如果LEFT_OF对应的View是null或者gone alignWithParentIfMissing这个值就会起效
    //它会把RelativeLayout当做被依赖的对象
    if (myWidth >= 0) {
      //如果父容器的宽度大于等于0
      //子View的右边界就是父容器的宽度减去paddingRight值和子View的MarginRight值
      childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
    }
  }

  //取得的当前子View的RIGHT_OF属性对应的View 处理逻辑同LEFT_OF相似
  anchorParams = getRelatedViewParams(rules, RIGHT_OF);
  if (anchorParams != null) {
    childParams.mLeft = anchorParams.mRight + (anchorParams.rightMargin +
                                               childParams.leftMargin);
  } else if (childParams.alignWithParent && rules[RIGHT_OF] != 0) {
    childParams.mLeft = mPaddingLeft + childParams.leftMargin;
  }

  //取得的当前子View的ALIGN_LEFT属性对应的View 处理逻辑同LEFT_OF相似
  anchorParams = getRelatedViewParams(rules, ALIGN_LEFT);
  if (anchorParams != null) {
    childParams.mLeft = anchorParams.mLeft + childParams.leftMargin;
  } else if (childParams.alignWithParent && rules[ALIGN_LEFT] != 0) {
    childParams.mLeft = mPaddingLeft + childParams.leftMargin;
  }

  //取得的当前子View的ALIGN_RIGHT属性对应的View 处理逻辑同LEFT_OF相似
  anchorParams = getRelatedViewParams(rules, ALIGN_RIGHT);
  if (anchorParams != null) {
    childParams.mRight = anchorParams.mRight - childParams.rightMargin;
  } else if (childParams.alignWithParent && rules[ALIGN_RIGHT] != 0) {
    if (myWidth >= 0) {
      childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
    }
  }

  //对属性android:alignParentLeft进行处理
  if (0 != rules[ALIGN_PARENT_LEFT]) {
    //当前子View的left值为PaddingLeft和其自身的MarginLeft值相加
    childParams.mLeft = mPaddingLeft + childParams.leftMargin;
  }

  //对属性android:alignParentRighth进行处理
  if (0 != rules[ALIGN_PARENT_RIGHT]) {
    //父容器宽度大于0
    if (myWidth >= 0) {
      //当前子View的right值为父容器宽度减去PaddingRight和自身的MarginLeft
      childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
    }
  }
}
private void measureChildHorizontal(
  View child, LayoutParams params, int myWidth, int myHeight) {
  //获得子View的宽度MeasureSpec
  final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
                                                        params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
                                                        myWidth);

  final int childHeightMeasureSpec;

  //android版本为4.2及其以下时mAllowBrokenMeasureSpecs == true
  if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
    if (params.height >= 0) {
      //高度确定,直接以MeasureSpec.EXACTLY测量
      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
        params.height, MeasureSpec.EXACTLY);
    } else {
      // Negative values in a mySize/myWidth/myWidth value in
      // RelativeLayout measurement is code for, "we got an
      // unspecified mode in the RelativeLayout's measure spec."
      // Carry it forward.

      //高度不确定
      childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
    }
  } else {
    final int maxHeight;
    //在旧版本(4.2及其以下)的平台上有在水平测量期间计算子view的高度时不会考虑margins和padding
    if (mMeasureVerticalWithPaddingMargin) {
      maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
                           - params.topMargin - params.bottomMargin);
    } else {
      maxHeight = Math.max(0, myHeight);
    }

    final int heightMode;
    //如果子View的宽度是精确模式(MATCH_PARENT或者dimens),那么它的高度也是精确模式
    if (params.height == LayoutParams.MATCH_PARENT) {
      heightMode = MeasureSpec.EXACTLY;
    } else {
      //如果子View的宽度是AT_MOST模式,那么它的高度的也是AT_MOST模式
      heightMode = MeasureSpec.AT_MOST;
    }
    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
  }
  //当宽和高都确定以后就可以开始测量了
  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
/**
 * 确定水平方向的子View的位置
 *
 * @param child
 * @param params
 * @param myWidth
 * @param wrapContent
 * @return
 */
private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
                                        boolean wrapContent) {
  //获取布局方向
  //RTL(从右向左)
  //LTR(默认情况:从左向右)
  final int layoutDirection = getLayoutDirection();
  //获取规则
  int[] rules = params.getRules(layoutDirection);

  if (params.mLeft == VALUE_NOT_SET && params.mRight != VALUE_NOT_SET) {
    // Right is fixed, but left varies
    // 左边界为无效值,右边界确定
    params.mLeft = params.mRight - child.getMeasuredWidth();
  } else if (params.mLeft != VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
    // Left is fixed, but right varies
    // 左边界确定,右边界为无效值
    params.mRight = params.mLeft + child.getMeasuredWidth();
  } else if (params.mLeft == VALUE_NOT_SET && params.mRight == VALUE_NOT_SET) {
    // Both left and right vary
    // 左右边界均为无效值
    // 如果设置了CENTER_IN_PARENT或者CENTER_HORIZONTAL
    if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
      // 如果不是wrap模式(一般就是match/dimens)
      if (!wrapContent) {
        //水平居中
        centerHorizontal(child, params, myWidth);
      } else {
        //wrap模式
        params.mLeft = mPaddingLeft + params.leftMargin;
        params.mRight = params.mLeft + child.getMeasuredWidth();
      }
      return true;
    } else {
      // This is the default case. For RTL we start from the right and for LTR we start
      // from the left. This will give LEFT/TOP for LTR and RIGHT/TOP for RTL.
      // RTL模式
      if (isLayoutRtl()) {
        params.mRight = myWidth - mPaddingRight - params.rightMargin;
        params.mLeft = params.mRight - child.getMeasuredWidth();
      } else {
        // TRL模式
        params.mLeft = mPaddingLeft + params.leftMargin;
        params.mRight = params.mLeft + child.getMeasuredWidth();
      }
    }
  }
  return rules[ALIGN_PARENT_END] != 0;
}
  • 4.处理垂直方向的子View
//垂直子View的集合
views = mSortedVerticalChildren;
count = views.length;
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;

for (int i = 0; i < count; i++) {
  final View child = views[i];
  if (child.getVisibility() != GONE) {
    final LayoutParams params = (LayoutParams) child.getLayoutParams();
    //把垂直方向的关系转换成边界
    applyVerticalSizeRules(params, myHeight, child.getBaseline());
    //测量垂直方向的子View 与measureChildHorizontal不同
    measureChild(child, params, myWidth, myHeight);
    //确定垂直方向的子View
    if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
      offsetVerticalAxis = true;
    }

    //当width为wrapContent时做特殊处理
    if (isWrapContentWidth) {
      //判断布局是RTL还是LTR模式
      if (isLayoutRtl()) {
        //RTL 根据前面的兼容性处理 需要对4.3以后的版本进行margin处理
        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
          width = Math.max(width, myWidth - params.mLeft);
        } else {
          width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
        }
      } else {
        //LTR
        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
          width = Math.max(width, params.mRight);
        } else {
          width = Math.max(width, params.mRight + params.rightMargin);
        }
      }
    }

    //当height为wrapContent时做特殊处理
    if (isWrapContentHeight) {
      //根据前面的兼容性处理 需要对4.3以后的版本进行margin处理
      if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
        height = Math.max(height, params.mBottom);
      } else {
        height = Math.max(height, params.mBottom + params.bottomMargin);
      }
    }

    //左上边界值计算
    if (child != ignore || verticalGravity) {
      left = Math.min(left, params.mLeft - params.leftMargin);
      top = Math.min(top, params.mTop - params.topMargin);
    }

    //右下边界值计算
    if (child != ignore || horizontalGravity) {
      right = Math.max(right, params.mRight + params.rightMargin);
      bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
    }
  }
}

private void applyVerticalSizeRules(LayoutParams childParams, int myHeight, int myBaseline) {
  final int[] rules = childParams.getRules();

  // Baseline alignment overrides any explicitly specified top or bottom.
  // 基准线对齐覆盖任何指定的顶部或者底部
  int baselineOffset = getRelatedViewBaselineOffset(rules);
  if (baselineOffset != -1) {
    if (myBaseline != -1) {
      baselineOffset -= myBaseline;
    }
    childParams.mTop = baselineOffset;
    childParams.mBottom = VALUE_NOT_SET;
    return;
  }

  // 基准线的偏移量 == -1的情况
  RelativeLayout.LayoutParams anchorParams;

  //默认值
  childParams.mTop = VALUE_NOT_SET;
  childParams.mBottom = VALUE_NOT_SET;

  //ABOVE属性对应的View的布局参数
  anchorParams = getRelatedViewParams(rules, ABOVE);
  if (anchorParams != null) {
    //当前子View的布局底部边界为ABOVE对应的顶部减去上下margin之和
    childParams.mBottom = anchorParams.mTop - (anchorParams.topMargin +
                                               childParams.bottomMargin);
  } else if (childParams.alignWithParent && rules[ABOVE] != 0) {
    //如果alignWithParent == true 并且也 ABOVE属性存在
    //alignWithParent的值对应的是alignWithParentIfMissing
    //如果ABOVE对应的View是null或者gone alignWithParentIfMissing这个值就会起效
    //它会把RelativeLayout当做被依赖的对象
    if (myHeight >= 0) {
      //如果父容器的宽度大于等于0
      //子View的右边界就是父容器的宽度减去paddingRight值和子View的MarginBottom值
      childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
    }
  }

  //BELOW属性对应的View的布局参数 处理逻辑同ABOVE相似
  anchorParams = getRelatedViewParams(rules, BELOW);
  if (anchorParams != null) {
    childParams.mTop = anchorParams.mBottom + (anchorParams.bottomMargin +
                                               childParams.topMargin);
  } else if (childParams.alignWithParent && rules[BELOW] != 0) {
    childParams.mTop = mPaddingTop + childParams.topMargin;
  }

  //ALIGN_TOP属性对应的View的布局参数 处理逻辑同ABOVE相似
  anchorParams = getRelatedViewParams(rules, ALIGN_TOP);
  if (anchorParams != null) {
    childParams.mTop = anchorParams.mTop + childParams.topMargin;
  } else if (childParams.alignWithParent && rules[ALIGN_TOP] != 0) {
    childParams.mTop = mPaddingTop + childParams.topMargin;
  }

  //ALIGN_BOTTOM属性对应的View的布局参数 处理逻辑同ABOVE相似
  anchorParams = getRelatedViewParams(rules, ALIGN_BOTTOM);
  if (anchorParams != null) {
    childParams.mBottom = anchorParams.mBottom - childParams.bottomMargin;
  } else if (childParams.alignWithParent && rules[ALIGN_BOTTOM] != 0) {
    if (myHeight >= 0) {
      childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
    }
  }

  //alignParentTop属性
  if (0 != rules[ALIGN_PARENT_TOP]) {
    childParams.mTop = mPaddingTop + childParams.topMargin;
  }

  //alignParentBottom属性
  if (0 != rules[ALIGN_PARENT_BOTTOM]) {
    if (myHeight >= 0) {
      childParams.mBottom = myHeight - mPaddingBottom - childParams.bottomMargin;
    }
  }
}

/**
 * Measure a child. The child should have left, top, right and bottom information
 * stored in its LayoutParams. If any of these values is VALUE_NOT_SET it means
 * that the view can extend up to the corresponding edge.
 *
 * @param child    Child to measure 需要测量的子view
 * @param params   LayoutParams associated with child 与子view关联的布局参数
 * @param myWidth  Width of the the RelativeLayout 父布局的width
 * @param myHeight Height of the RelativeLayout 父布局的height
 */
private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
  int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
                                                  params.mRight, params.width,
                                                  params.leftMargin, params.rightMargin,
                                                  mPaddingLeft, mPaddingRight,
                                                  myWidth);
  int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
                                                   params.mBottom, params.height,
                                                   params.topMargin, params.bottomMargin,
                                                   mPaddingTop, mPaddingBottom,
                                                   myHeight);
  child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

/**
 * Get a measure spec that accounts for all of the constraints on this view.
 * This includes size constraints imposed by the RelativeLayout as well as
 * the View's desired dimension.
 *
 * @param childStart   The left or top field of the child's layout params
 *                     子View布局的左侧或者顶部字段
 * @param childEnd     The right or bottom field of the child's layout params
 *                     子View布局的右侧或底部字段
 * @param childSize    The child's desired size (the width or height field of
 *                     the child's layout params)
 * @param startMargin  The left or top margin
 *                     左侧或者顶部的Margin值
 * @param endMargin    The right or bottom margin
 *                     右侧或者底部的Margin值
 * @param startPadding mPaddingLeft or mPaddingTop
 *                     左侧或者顶部的Padding值
 * @param endPadding   mPaddingRight or mPaddingBottom
 *                     右侧或者底部的Padding值
 * @param mySize       The width or height of this view (the RelativeLayout)
 *                     父布局的width或者height值
 * @return MeasureSpec for the child
 */
private int getChildMeasureSpec(int childStart, int childEnd,
                                int childSize, int startMargin, int endMargin, int startPadding,
                                int endPadding, int mySize) {
  int childSpecMode = 0;
  int childSpecSize = 0;

  // Negative values in a mySize value in RelativeLayout
  // measurement is code for, "we got an unspecified mode in the
  // RelativeLayout's measure spec."
  // RelativeLayout测量中mySize值为负值是因为“在RelativeLayout测量规范中我们得到了未指定的模式”。
  final boolean isUnspecified = mySize < 0;
  // 如果父容器的宽度的小于0并且系统版本大于4.3
  if (isUnspecified && !mAllowBrokenMeasureSpecs) {
    //子View的左右边间距都不等于VALUE_NOT_SET
    if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
      // Constraints fixed both edges, so child has an exact size.
      // 边界确定的话 子View的size也是个精确值
      childSpecSize = Math.max(0, childEnd - childStart);
      childSpecMode = MeasureSpec.EXACTLY;
    } else if (childSize >= 0) {
      // 如果不满足第一个条件但是childSize>=0 那么子View也是精确值
      // The child specified an exact size.
      childSpecSize = childSize;
      childSpecMode = MeasureSpec.EXACTLY;
    } else {
      // 都不满足的话就是不确定值
      // Allow the child to be whatever size it wants.
      childSpecSize = 0;
      childSpecMode = MeasureSpec.UNSPECIFIED;
    }

    return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
  }

  //mySize >= 0 的情况

  // Figure out start and end bounds.
  int tempStart = childStart;
  int tempEnd = childEnd;

  // If the view did not express a layout constraint for an edge, use
  // view's margins and our padding
  // 如果没有指定边界值,设置一个默认值
  if (tempStart == VALUE_NOT_SET) {
    tempStart = startPadding + startMargin;
  }
  if (tempEnd == VALUE_NOT_SET) {
    tempEnd = mySize - endPadding - endMargin;
  }

  // Figure out maximum size available to this view
  // 指明最大可用空间
  final int maxAvailable = tempEnd - tempStart;


  //当左右边界都是确定值时 基本上已经可用确定为精确模式和大小了
  //特殊情况是isUnspecified = true && mAllowBrokenMeasureSpecs = true
  if (childStart != VALUE_NOT_SET && childEnd != VALUE_NOT_SET) {
    // Constraints fixed both edges, so child must be an exact size.
    childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
    childSpecSize = Math.max(0, maxAvailable);
  } else {

    if (childSize >= 0) {
      //确定大小的情况
      // Child wanted an exact size. Give as much as possible.
      childSpecMode = MeasureSpec.EXACTLY;

      if (maxAvailable >= 0) {
        // We have a maximum size in this dimension.
        childSpecSize = Math.min(maxAvailable, childSize);
      } else {
        // We can grow in this dimension.
        childSpecSize = childSize;
      }
    } else if (childSize == LayoutParams.MATCH_PARENT) {
      // MATCH_PARENT情况
      // Child wanted to be as big as possible. Give all available
      // space.
      childSpecMode = isUnspecified ? MeasureSpec.UNSPECIFIED : MeasureSpec.EXACTLY;
      childSpecSize = Math.max(0, maxAvailable);
    } else if (childSize == LayoutParams.WRAP_CONTENT) {
      // WRAP_CONTENT情况
      // Child wants to wrap content. Use AT_MOST to communicate
      // available space if we know our max size.
      if (maxAvailable >= 0) {
        // We have a maximum size in this dimension.
        childSpecMode = MeasureSpec.AT_MOST;
        childSpecSize = maxAvailable;
      } else {
        // We can grow in this dimension. Child can be as big as it
        // wants.
        childSpecMode = MeasureSpec.UNSPECIFIED;
        childSpecSize = 0;
      }
    }
  }

  return MeasureSpec.makeMeasureSpec(childSpecSize, childSpecMode);
}
  • 5.基准线计算
View baselineView = null;
LayoutParams baselineParams = null;
for (int i = 0; i < count; i++) {
  final View child = views[i];
  if (child.getVisibility() != GONE) {
    final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
    if (baselineView == null || baselineParams == null
        || compareLayoutPosition(childParams, baselineParams) < 0) {
      baselineView = child;
      baselineParams = childParams;
    }
  }
}
mBaselineView = baselineView;
  • 6.对wrap_content模式进行处理
//如果width是wrap模式
if (isWrapContentWidth) {
  // Width already has left padding in it since it was calculated by looking at
  // the right of each child view
  // 宽度已经根据每个子View的右边进行了填充
  width += mPaddingRight;

  if (mLayoutParams != null && mLayoutParams.width >= 0) {
    width = Math.max(width, mLayoutParams.width);
  }

  width = Math.max(width, getSuggestedMinimumWidth());
  width = resolveSize(width, widthMeasureSpec);

  //在得到最终width之后,就对依赖RelativeLayout的子view加上偏移量
  if (offsetHorizontalAxis) {
    for (int i = 0; i < count; i++) {
      final View child = views[i];
      //视图可见
      if (child.getVisibility() != GONE) {
        //获取布局参数
        final LayoutParams params = (LayoutParams) child.getLayoutParams();
        //获取规则
        final int[] rules = params.getRules(layoutDirection);
        //如果设置了CENTER_IN_PARENT或者CENTER_HORIZONTAL属性
        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
          centerHorizontal(child, params, width);
        } else if (rules[ALIGN_PARENT_RIGHT] != 0) {
          //如果设置了ALIGN_PARENT_RIGHT属性
          final int childWidth = child.getMeasuredWidth();
          params.mLeft = width - mPaddingRight - childWidth;
          params.mRight = params.mLeft + childWidth;
        }
      }
    }
  }
}

//如果height是wrap模式 同上
if (isWrapContentHeight) {
  // Height already has top padding in it since it was calculated by looking at
  // the bottom of each child view
  height += mPaddingBottom;

  if (mLayoutParams != null && mLayoutParams.height >= 0) {
    height = Math.max(height, mLayoutParams.height);
  }

  height = Math.max(height, getSuggestedMinimumHeight());
  height = resolveSize(height, heightMeasureSpec);

  if (offsetVerticalAxis) {
    for (int i = 0; i < count; i++) {
      final View child = views[i];
      if (child.getVisibility() != GONE) {
        final LayoutParams params = (LayoutParams) child.getLayoutParams();
        final int[] rules = params.getRules(layoutDirection);
        if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
          centerVertical(child, params, height);
        } else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
          final int childHeight = child.getMeasuredHeight();
          params.mTop = height - mPaddingBottom - childHeight;
          params.mBottom = params.mTop + childHeight;
        }
      }
    }
  }
}
  • 7.根据gravity对布局参数修正
//根据水平和垂直方向的gravity进行布局参数修正
if (horizontalGravity || verticalGravity) {
  final Rect selfBounds = mSelfBounds;
  selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
                 height - mPaddingBottom);

  final Rect contentBounds = mContentBounds;
  Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
                layoutDirection);

  final int horizontalOffset = contentBounds.left - left;
  final int verticalOffset = contentBounds.top - top;
  if (horizontalOffset != 0 || verticalOffset != 0) {
    for (int i = 0; i < count; i++) {
      final View child = views[i];
      if (child.getVisibility() != GONE && child != ignore) {
        final LayoutParams params = (LayoutParams) child.getLayoutParams();
        if (horizontalGravity) {
          params.mLeft += horizontalOffset;
          params.mRight += horizontalOffset;
        }
        if (verticalGravity) {
          params.mTop += verticalOffset;
          params.mBottom += verticalOffset;
        }
      }
    }
  }
}
  • 8.RTL模式对布局参数进行修正
//RTL模式下对布局参数进行修正
if (isLayoutRtl()) {
  final int offsetWidth = myWidth - width;
  for (int i = 0; i < count; i++) {
    final View child = views[i];
    if (child.getVisibility() != GONE) {
      final LayoutParams params = (LayoutParams) child.getLayoutParams();
      params.mLeft -= offsetWidth;
      params.mRight -= offsetWidth;
    }
  }
}

onLayout过程

  • 做法其实很简单,取出每个已经measure的子View的布局参数,然后根据左上右下确定布局。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
  //  The layout has actually already been performed and the positions
  //  cached.  Apply the cached values to the children.
  //  布局实际上已经执行,位置缓存。将缓存的值应用于孩子

  final int count = getChildCount();
  for (int i = 0; i < count; i++) {
    View child = getChildAt(i);
    if (child.getVisibility() != GONE) {
      RelativeLayout.LayoutParams st =
        (RelativeLayout.LayoutParams) child.getLayoutParams();
      child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
    }
  }
}

onDraw过程

在源码中并没有重载方法,因为RelativeLayout起的是一个容器的作用。

站在巨人的肩膀上才能看的更高更远
参考博客:http://blog.csdn.net/wz249863091/article/details/51757069

你可能感兴趣的:(RelativeLayout源码详解)