RelativeLayout源码详解

528 阅读5分钟

RelativeLayout源码详解

需要关注的点

1:RelativeLayout进行两次测量

源码分析

一般所有控件类的源码,都会从 measure, layout和draw3个方法入手,查看他们的回调函数onMeasure, onLayout和onDraw 只要明白这3个流程,一般控件的整个实现也就明白了 LinearLayout作为一个ViewGroup的子类,主要作为一个布局容器出现,所以我们需要重点查看onMeasure方法,

if (mDirtyHierarchy) {
    mDirtyHierarchy = false;
    sortChildren();
}
    

首先根据mDirtyHierarchy来判断是否需要将子控件重新进行排序

public void requestLayout() {
    super.requestLayout();
    mDirtyHierarchy = true;
}

mDirtyHierarchy只有在requestLayout方法下进行修改,当mDirtyHierarchy为true时,调用sortChildren方式将childview进行重新排序

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));
    }

    graph.getSortedViews(mSortedVerticalChildren, RULES_VERTICAL);
    graph.getSortedViews(mSortedHorizontalChildren, RULES_HORIZONTAL);
}

根据类名DependencyGraph,这是一个依赖关系图,主要是add方法和getSortedViews
先看看add方法

void add(View view) {
    // 因为是图,根据view来生成节点,
    final int id = view.getId();
    final Node node = Node.acquire(view);
    // 如果是有效ID,则将该节点添加到list中
    if (id != View.NO_ID) {
        mKeyNodes.put(id, node);
    }

    mNodes.add(node);
}

再看看getSortedViews方法

void getSortedViews(View[] sorted, int... rules) {
    // 当view找不到其他可依赖的view时,作为root节点
    final ArrayDeque<Node> roots = findRoots(rules);
    int index = 0;

    Node node;
    // 读取root的下一个node
    while ((node = roots.pollLast()) != null) {
        final View view = node.view;
        final int key = view.getId();
        
        // 将符合规则的view加入sorted中
        sorted[index++] = view;
        
        // dependents 依赖该node的node  (A C依赖B 则B的dependents中存A C)
        final ArrayMap<Node, DependencyGraph> 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 则dependencies存有B D )
            final SparseArray<Node> dependencies = dependent.dependencies;
            
            //移除当前node和dependencies的依赖关系
            dependencies.remove(key);
            if (dependencies.size() == 0) {
                //如果解除依赖后没有其它依赖 则将该node也视为rootNode
                roots.add(dependent);
            }
        }
    }

        if (index < sorted.length) {
            throw new IllegalStateException("Circular dependencies cannot exist"
                    + " in RelativeLayout");
        }
    }
    

举个简单的例子,A依赖B B依赖C 首先存入C 因为C不依赖任何其它的,在遍历依赖于C的的node时,dependencies中只有B,在将B的该依赖删掉后,B不再依赖其他任何的node,被添加到roots中,然后再遍历依赖于B的node时,dependencies中只有A,在将A的该依赖删掉后,A不再依赖其他任何的node,被添加到roots中,最后遍历依赖于A的node时,A没有依赖与被依赖,循环结果,sorted中的顺序为C B A

private ArrayDeque<Node> findRoots(int[] rulesFilter) {
        //keyNodes为nodelist
        final SparseArray<Node> keyNodes = mKeyNodes;
        final ArrayList<Node> 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
        //Find root可以多次调用,因此请确保清除
        //运行算法之前的所有依赖项和依赖项
        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
        // 为图形的每个节点建立依赖项和依赖项
        for (int i = 0; i < count; i++) {
            final Node node = nodes.get(i);

            final LayoutParams layoutParams = (LayoutParams) node.view.getLayoutParams();
            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
            //只查看参数中传递的规则,这样我们只构建
            //特定规则集的依赖项
            
            //遍历所有node  存入当前view和他所依赖的关系
            for (int j = 0; j < rulesCount; j++) {
                // rule对应被依赖view的id
                final int rule = rules[rulesFilter[j]];
                if (rule > 0 || ResourceId.isValid(rule)) {
                    // The node this node depends on
                    // 此节点所依赖的节点
                    final Node dependency = keyNodes.get(rule);
                    // Skip unknowns and self dependencies
                    // 跳过未知和自我依赖
                    if (dependency == null || dependency == node) {
                        continue;
                    }
                    // 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<Node> roots = mRoots;
        roots.clear();

        // Finds all the roots in the graph: all nodes with no dependencies
        // 查找图中的所有根:没有依赖关系的所有节点
        
        // 再次遍历  如果该node的依赖关系为0 即该view不依赖任何view 则视为rootView
        for (int i = 0; i < count; i++) {
            final Node node = nodes.get(i);
            if (node.dependencies.size() == 0) roots.addLast(node);
        }

        return roots;
    }
    

整个排序过程是一个拓扑关系图,但是我对拓扑关系图没有什么研究,在此不做过多展开

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;
// 记录我们的尺寸,如果他们是已知的

// 如果不是UNSPECIFIED模式 则将widthSize赋值于myWidth
if (widthMode != MeasureSpec.UNSPECIFIED) {
    myWidth = widthSize;
}

// 如果不是UNSPECIFIED模式 则将heightSize赋值于myHeight
if (heightMode != MeasureSpec.UNSPECIFIED) {
    myHeight = heightSize;
}

//如果是EXACTLY模式 则将myWidth和myHeight记录
if (widthMode == MeasureSpec.EXACTLY) {
    width = myWidth;
}

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

View ignore = null;
//判断是否为Start 和  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);
}

//宽度和高度是否为warp模式
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”。

//在计算和分配的子View的坐标的时候 需要用到父VIew的尺寸 但是暂时无法拿到准确值(待完成下面操作)
//先使用默认值代替 在计算后 用偏移量更新真是坐标
final int layoutDirection = getLayoutDirection();
if (isLayoutRtl() && myWidth == -1) {
    myWidth = DEFAULT_WIDTH;
}

进行一些参数的初始化

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;
        }
    }
}

遍历水平关系的view
先看看applyHorizontalSizeRules方法

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_SETT表示该方向的“软需求”。例如:
    //left=10,right=VALUE_NOT_SETT表示视图必须从10开始,但可以走到最远
    //想去右边
    //left=VALUE_NOT_SETT,right=10表示视图必须在10处结束,但可以走到最远
    //想往左边走
    //左=10,右=20表示左右两端都是固定的
    childParams.mLeft = VALUE_NOT_SET;
    childParams.mRight = VALUE_NOT_SET;

    //得到当前子View的layout_toLeftOf属性对应的View
    anchorParams = getRelatedViewParams(rules, LEFT_OF);
    if (anchorParams != null) {
    //如果这个属性存在 则当前子View的右坐标是layout_toLeftOf对应的view的左坐标减去对应view的marginLeft的值和自身marginRight的值
        childParams.mRight = anchorParams.mLeft - (anchorParams.leftMargin +
                childParams.rightMargin);
    } else if (childParams.alignWithParent && rules[LEFT_OF] != 0) {
        //如果alignWithParent为true alignWithParent取alignWithParentIfMissing
        //如果layout_toLeftOf的view为空 或者gone 则将RelativeLayout当做被依赖的对象
        
        //如果父容器RelativeLayout的宽度大于0
        //则子View的右坐标为 父RelativeLayout的宽度减去 mPaddingRight 和自身的marginRight
        if (myWidth >= 0) {
            childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
        }
    }

    //类似的方法 得到左坐标(通过参数RIGHT_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;
    }

    //类似的方法 得到左坐标(通过参数ALIGN_LEFT)
    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;
    }
    
    //类似的方法 得到左坐标(通过参数ALIGN_RIGHT)
    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;
        }
    }

    //根据ALIGN_PARENT_LEFT 将自己放到父RelativeLayout的左边
    if (0 != rules[ALIGN_PARENT_LEFT]) {
        childParams.mLeft = mPaddingLeft + childParams.leftMargin;
    }

    //根据ALIGN_PARENT_RIGHT 将自己放到父RelativeLayout的右边
    if (0 != rules[ALIGN_PARENT_RIGHT]) {
        if (myWidth >= 0) {
            childParams.mRight = myWidth - mPaddingRight - childParams.rightMargin;
        }
    }
}

applyHorizontalSizeRules这个方法主要是通过水平上的依赖规则对params的right和left进行计算赋值
在看看measureChildHorizontal方法

private void measureChildHorizontal(
        View child, LayoutParams params, int myWidth, int myHeight) {
    // 获得child的宽度MeasureSpec
    final int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft, params.mRight,
            params.width, params.leftMargin, params.rightMargin, mPaddingLeft, mPaddingRight,
            myWidth);

    final int childHeightMeasureSpec;
    // 在低于4.2的时候 mAllowBrokenMeasureSpecs为true
    //当myHeight < 0 时 则根据父RelativeLayout设置其MeasureSpec模式
    if (myHeight < 0 && !mAllowBrokenMeasureSpecs) {
        // 如果父RelativeLayout的height大于0  则 设置子view的MeasureSpec模式为EXACTLY
        if (params.height >= 0) {
            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.
            
            // 反之 如果其小于0  则设置子View的MeasureSpec为UNSPECIFIED
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
        }
    } else {
        // 当前myHeight >= 0
        // 判断当前高度是否与父RelativeLayout高度相同 设置heightMode
        // 根据maxHeight 和heightMode设置子View的MeasureSpec模式
        final int maxHeight;
        if (mMeasureVerticalWithPaddingMargin) {
            maxHeight = Math.max(0, myHeight - mPaddingTop - mPaddingBottom
                    - params.topMargin - params.bottomMargin);
        } else {
            maxHeight = Math.max(0, myHeight);
        }

        final int heightMode;
        if (params.height == LayoutParams.MATCH_PARENT) {
            heightMode = MeasureSpec.EXACTLY;
        } else {
            heightMode = MeasureSpec.AT_MOST;
        }
        childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, heightMode);
    }
    
    //获得了子View的WidthMeasureSpec和HeightMeasureSpec
    //子View可以通过measure方法获取自身的size
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

measureChildHorizontal 方法里面主要是获取到WidthMeasureSpec和HeightMeasureSpec后调用child.mesuare方法来测量水平方向上面的childview的尺寸
再看看positionChildHorizontal方法

private boolean positionChildHorizontal(View child, LayoutParams params, int myWidth,
        boolean wrapContent) {
    // 获取RelativeLayout的布局方向
    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) {
            if (!wrapContent) {
                //非wrap情况下
                //把子View水平中心固定在RelativeLayout的中心
                centerHorizontal(child, params, myWidth);
            } else {
                // 这个方法比较简单,就不单独说了,这里是根据RLT判断
                // 如果isLayoutRtl为true,那就先算右边距,右边距为myWidth - mPaddingRight - params.rightMargin 
                // 左边距为右边距加上测量宽度,如果isLayoutRtl为false,那就先算左边距,左
                // 边距为mPaddingRight + params.rightMargin,右边距为左边距加上测量宽度
                positionAtEdge(child, params, myWidth);
            }
            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,我们从右边开始,对于LTR,我们从右边开始
            //从左边。这将为LTR提供LEFT/TOP,为RTL提供RIGHT/TOP。
            
            具体描述,上面有写
            positionAtEdge(child, params, myWidth);
        }
    }
    // 当为CENTER_IN_PARENT  CENTER_HORIZONTAL ALIGN_PARENT_END三种情况之一时返回True
    return rules[ALIGN_PARENT_END] != 0;
}

positionChildHorizontal 方法确定水平方向子View的位置,最后返回childview是否水平居中

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
        measureChild(child, params, myWidth, myHeight);
        //确定竖直方向子View的位置
        if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
            offsetVerticalAxis = true;
        }

        //首先判断是否为wrap模式
        if (isWrapContentWidth) {
            if (isLayoutRtl()) {
                //根据RTL或者LTR和版本进行区分
                //Build.VERSION_CODES.KITKAT = 19
                //主要对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 {
                if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                    width = Math.max(width, params.mRight);
                } else {
                    width = Math.max(width, params.mRight + params.rightMargin);
                }
            }
        }

        if (isWrapContentHeight) {
            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);
        }
    }
}

垂直方向上面与水平方向上面大体相识,不再重复,有区别的在于,垂直方向上面再确定位置之后,还需要对margin进行一定的处理

// Use the top-start-most laid out view as the baseline. RTL offsets are
// applied later, so we can use the left-most edge as the starting edge.
//使用最顶层的布局视图作为基线。RTL偏移为
//稍后应用,因此我们可以使用最左边的边作为起始边。
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;

baseline计算

//如果是wrap模式
if (isWrapContentWidth) {
    // Width already has left padding in it since it was calculated by looking at
    // the right of each child 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的子View重测
                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;
                }
            }
        }
    }
}

// 同上
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;
                }
            }
        }
    }
}

// 根据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;
                }
            }
        }
    }
}

//如果是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;
        }
    }
}

setMeasuredDimension(width, height);

小结

  1. RelativeLayout的两次测量是因为在水平方向和垂直方向上面都要进行一次测量
  2. RelativeLayout更加关注子View的left right top bottom值 并且优先级高于width和height