人生如逆旅,我亦是行人.
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<Node> mNodes = new ArrayList<Node>();
/**
* List of nodes in the graph. Each node is identified by its
* view id (see View#getId()).
* 图中的所有节点都会有一个特定的id
*/
private SparseArray<Node> mKeyNodes = new SparseArray<Node>();
/**
* Temporary data structure used to build the list of roots
* for this graph.
* <p>
* 临时数据结构用于构建此图的根列表。
*/
private ArrayDeque<Node> mRoots = new ArrayDeque<Node>();
/**
* Clears the graph.
*/
void clear() {
final ArrayList<Node> 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.
* <p>
* 创建一个有序的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<Node> 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<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才能确定位置,那么dependcies = B,D
final SparseArray<Node> 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<Node> findRoots(int[] rulesFilter) {
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
//查找根节点可能或被调用多次,因此要在运行算法之前确保清除所有依赖关系
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<Node> 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.
* <p>
* 依赖图中的一个节点。 节点是一个View,其依赖关系列表及其依赖列表。
* <p>
* 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<Node, DependencyGraph> dependents =
new ArrayMap<Node, DependencyGraph>();
/**
* The list of dependencies for this node.
*
* 依赖该节点的列表。
*/
final SparseArray<Node> dependencies = new SparseArray<Node>();
/*
* 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<Node> sPool =
new SynchronizedPool<Node>(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