Java最新LinearLayout源码详解,zookeeper面试

Java核心架构进阶知识点

面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Java核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、Spring相关、分布式、微服务、RPC、网络、设计模式、MQ、Redis、MySQL、设计模式、负载均衡、算法、数据结构、kafka、ZK、集群等。而这些也全被整理浓缩到了一份pdf——《Java核心架构进阶知识点整理》,全部都是精华中的精华,本着共赢的心态,好东西自然也是要分享的

image

image

image

内容颇多,篇幅却有限,这就不在过多的介绍了,大家可根据以上截图自行脑补

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
//确定这个孩子想要多大。如果这个或前面的孩子给了一个权重,那么我们允许它使用所有可用的空间
//(如果需要,我们将在稍后缩小内容)。
final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);

// 重置子控件高度,然后进行精确赋值
final int childHeight = child.getMeasuredHeight();
if (useExcessSpace) {
// Restore the original height and record how much space
// we’ve allocated to excess-only children so that we can
// match the behavior of EXACTLY measurement.
//恢复原来的高度,并记录我们分配给 excess-only children 的空间大小,以便我们能够准确地匹配测量的行为。
lp.height = 0;
consumedExcessSpace += childHeight;
}

final int totalLength = mTotalLength;
// getNextLocationOffset返回的永远是0,这里是加上子控件的margin值
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
// 如果设置了measureWithLargestChild属性为true,获取最高子控件的高度
if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}

useLargestChild 可以通过 xml 属性 android:measureWithLargestChild 设置的,含义是所有带权重属性的View都会使用最大View的最小尺寸

//useLargestChild 属性指定
//所以接下来根据 largestChildHeight 重新计算高度
if (useLargestChild &&
(heightMode == MeasureSpec.AT_MOST || heightMode == 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();
// Account for negative margins
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
}

使用 largestChildHeight 重新计算mTotalLength,在代码中也可以看到,这个属性只在wrap_content情况下生效

到第二次测量中间还有一些其他的计算,就不一一去看了,直接看第二次测量

// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds. If we skipped
// measurement on any children, we need to measure them now.
// 重新计算有 weight 属性的 childView 大小,
// 如果还有可用的空间,则扩展 childView,计算其大小
// 如果 childView 超出了 LinearLayout 的边界,则收缩 childView
int remainingExcess = heightSize - mTotalLength

  • (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace);
    if (skippedMeasure
    || ((sRemeasureWeightedChildren || remainingExcess != 0) && totalWeight > 0.0f)) {
    // 根据 mWeightSum 计算得到 remainingWeightSum,mWeightSum 是通过
    // android:weightSum` 属性设置的,totalWeight 是通过第一次 for 循环计算得到的
    float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

mTotalLength = 0;

for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null || child.getVisibility() == View.GONE) {
continue;
}

final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final float childWeight = lp.weight;
// 这是设置了 weight 的情况下,最重要的一行代码
// remainingExcess 剩余高度 * ( childView 的 weight / remainingWeightSum)
// share 便是此 childView 通过这个公式计算得到的高度,
// 并重新计算剩余高度 remainingExcess 和剩余权重总和 remainingWeightSum
if (childWeight > 0) {
final int share = (int) (childWeight * remainingExcess / remainingWeightSum);
remainingExcess -= share;
remainingWeightSum -= childWeight;

final int childHeight;
if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) {
childHeight = largestChildHeight;
} else if (lp.height == 0 && (!mAllowInconsistentMeasurement
|| heightMode == MeasureSpec.EXACTLY)) {
//如果是当前LinearLayout的模式是EXACTLY
//那么这个子View是没有被测量过的,就需要测量一次
//如果不是EXACTLY的,在第一次循环里就被测量一些了
// This child needs to be laid out from scratch using
// only its share of excess space.
childHeight = share;
} else {
//如果是非EXACTLY模式下的子View就再加上
//weight分配占比*剩余高度
// This child had some intrinsic height to which we
// need to add its share of excess space.
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));
}

final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);

boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
lp.width == LayoutParams.MATCH_PARENT;

alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);

allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}

// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;
// TODO: Should we recompute the heightSpec based on the new total length?
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);

// We have no limit, so make all weighted views as tall as the largest child.
// Children will have already been measured once.
if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
for (int i = 0; i < count; i++) {
final View child = getVirtualChildAt(i);
if (child == null || child.getVisibility() == View.GONE) {
continue;
}

final LinearLayout.LayoutParams lp =
(LinearLayout.LayoutParams) child.getLayoutParams();

float childExtra = lp.weight;
if (childExtra > 0) {
child.measure(
MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(largestChildHeight,
MeasureSpec.EXACTLY));
}
}
}
}

if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}

maxWidth += mPaddingLeft + mPaddingRight;

// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);

if (matchWidth) {
forceUniformWidth(count, heightMeasureSpec);
}
}

举例

在weight计算方面,我们可以清晰的看到,weight为何是针对剩余空间进行分配的原理了。 我们打个比方,假如现在我们的LinearLayout的weightSum=10,总高度100,有两个子控件(他们的height=0dp),他们的weight分别为2:8。

那么在测量第一个子控件的时候,可用的剩余高度为100,第一个子控件的高度则是100*(2/10)=20,接下来可用的剩余高度为80

我们继续第二个控件的测量,此时它的高度实质上是80*(8/8)=80

到目前为止,看起来似乎都是正确的,但关于weight我们一直有一个疑问:**就是我们为子控件给定height=0dp和height=match_parent时我们就会发现我们的子控件的高度比是不同的,前者是2:8而后者是调转过来变成8:2 **

对于这个问题,我们不妨继续看看代码。

接下来我们会看到这么一个分支:

if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) { } else {}

首先我们不管heightMode,也就是父类的测量模式,剩下一个判定条件就是lp.height,也就是子类的高度。

既然有针对这个进行判定,那就是意味着肯定在此之前对child进行过measure,事实上,在这里我们一早就对这个地方进行过描述,这个方法正是measureChildBeforeLayout()。

还记得我们的measureChildBeforeLayout()执行的先行条件吗

YA,just u see,正是不满足(LinearLayout的测量模式非EXACTLY/child.height0/child.weight/child.weight>0)之中的child.height0

因为除非我们指定height=0,否则match_parent是等于-1,wrap_content是等于-2.

在执行measureChildBeforeLayout(),由于我们的child的height=match_parent,因此此时可用空间实质上是整个LinearLayout,执行了measureChildBeforeLayout()后,此时的mTotalLength是整个LinearLayout的大小

回到我们的例子,假设我们的LinearLayout高度为100,两个child的高度都是match_parent,那么执行了measureChildBeforeLayout()后,我们两个子控件的高度都将会是这样:

child_1.height=100

child_2.height=100

mTotalLength=100+100=200

在一系列的for之后,执行到我们剩余空间:

int delta = heightSize - mTotalLength;

(delta=100[linearlayout的实际高度]-200=-100)

没错,你看到的的确是一个负数。

接下来就是套用weight的计算公式:

share=(int) (childExtra * delta / weightSum)

即:share=-100(2/10)=-20;*

然后走到我们所说的if/else里面

最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)

image

除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值