一.开篇
LinearLayout的使用比较简单,本身也没有关于事件分发的重写,layout_weight的设计是一个亮眼的地方,了解内部是如何工作的对于自己开发自定义View也会有很多帮助。
当height设置match_parent时,当height设置0时恰好相反,第一张图是match,第二张是0dp,当然wrap也是不一样的。
二.height设置0时 源码解析
LinearLayout有水平和垂直两种情况,这边分析垂直的情况,View的绘制流程这边也只关注onMeasure,其他情况都比较简单,按照 height==0 分析。 看下代码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
mOrientation就是XML中设置的,先看下一些局部变量
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0; // 子View的高度之和 不等于LinearLayout的高度
int maxWidth = 0; //
int childState = 0; //
int alternativeMaxWidth = 0; //
int weightedMaxWidth = 0; //
boolean allFillParent = true; //
float totalWeight = 0; //
final int count = getVirtualChildCount();
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean matchWidth = false;
boolean skippedMeasure = false;
final int baselineChildIndex = mBaselineAlignedChildIndex;
final boolean useLargestChild = mUseLargestChild;
int largestChildHeight = Integer.MIN_VALUE;
int consumedExcessSpace = 0;
int nonSkippedChildCount = 0;
....
}
再看下如何对这些变量赋值的,分为三次循环
2.1 第一次循环
// See how tall everyone is. Also remember max width.
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
// child为空 算作高度为0
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == View.GONE) {
// child 不显示的时候跳过
i += getChildrenSkipCount(child, i);
continue;
}
nonSkippedChildCount++;
if (hasDividerBeforeChildAt(i)) {
// 计算分割线的高度加入
mTotalLength += mDividerHeight;
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
totalWeight += lp.weight;
// 高度为0 weight大于0的时候为true
final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
// useExcessSpace为ture 时先不计算
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
skippedMeasure = true;
} else {
if (useExcessSpace) {
lp.height = LayoutParams.WRAP_CONTENT;
}
// 如果这个View之前的View有应用weight的,usedHeight就为0,(这种情况需要二次计算)
否则就是整个子组件的总高度
final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
final int childHeight = child.getMeasuredHeight();
if (useExcessSpace) {
// height == 0 的时候会重制height
lp.height = 0;
consumedExcessSpace += childHeight;
}
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}
上面这个过程可以看出当有weight的时候 会出现计算子View height过高的问题,当然这只是第一次循环。
2.2 第二次循环
// 这个循环的条件是 useLargestChild为ture 这个需要xml设置
if (useLargestChild &&
(widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
if (isExactly) {
mTotalLength += largestChildWidth + lp.leftMargin + lp.rightMargin +
getNextLocationOffset(child);
} else {
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildWidth +
lp.leftMargin + lp.rightMargin + getNextLocationOffset(child));
}
}
}
2.3 第三次循环
for (int i = 0; i < count; ++i) {
...
// mTotalLength == 0
int heightSize = mTotalLength;
// getSuggestedMinimumHeight 默认为0 需要自己设置 一般就是heightSize
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
// resolveSizeAndState 基本就是对父组件height 和 0进行位或操作 ,heightSize基本也不会发生改变
int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
// MEASURED_SIZE_MASK==0x00ffffff ,heightSize 和这个值与操作 ,因为height值不够大 计算结果肯定还是这个值
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
int remainingExcess = heightSize - mTotalLength
+ (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
if (childWeight > 0) {
// remainingExcess是剩下的空间 用于分配weight的控件
final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
remainingExcess -= share;
remainingWeightSum -= childWeight;
final int childHeight;
// 从这个if的走向可以看出 odp height的时候 按照weight分配 大的分配到大的height
if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) {
childHeight = largestChildHeight;
} else if (lp.height == 0 && (!mAllowInconsistentMeasurement
|| heightMode == MeasureSpec.EXACTLY)) {
childHeight = share;
} else {
// share可能为负值
childHeight = child.getMeasuredHeight() + share;
}
final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
Math.max(0, childHeight), MeasureSpec.EXACTLY);
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
lp.width);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
// Child may now not fit in vertical dimension. childState = combineMeasuredStates(childState, child.getMeasuredState()
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
}
...
}
三.当对于height == match_parent时 源码解析
这边只列出不同之处
3.1 第一次循环
totalHeight = 2*父组件的高度
3.2 第二次循环
一样
3.3 第三次循环
remainingExcess = -父组件的高度
四.当对于height == wrap_content时 源码解析
这边只列出不同之处
4.1 第一次循环
totalHeight 约等于 2*文本的高度
4.2 第二次循环
一样
4.3 第三次循环
remainingExcess = 父组件的高度-2*文本的高度
总结:
1.单个weight高度公式 :height = (父组件高度-子组件原高度之和)*weight/weightSum +单个子组件原高度
2.有weight的情况会执行两遍子view的measure
今天的文章被人忽略的LinearLayout分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/16062.html