移动开发最新【View系列】View的measure流程源码全解析,2024年最新面试跳槽原因怎么说

写在最后

由于本文罗列的知识点是根据我自身总结出来的,并且由于本人水平有限,无法全部提及,欢迎大神们能补充~

将来我会对上面的知识点一个一个深入学习,也希望有童鞋跟我一起学习,一起进阶。

提升架构认知不是一蹴而就的,它离不开刻意学习和思考。

**这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家,**梳理了多年的架构经验,筹备近1个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

最近还在整理并复习一些Android基础知识点,有问题希望大家够指出,谢谢。

希望读到这的您能转发分享和关注一下我,以后还会更新技术干货,谢谢您的支持!

转发+点赞+关注,第一时间获取最新知识点

Android架构师之路很漫长,一起共勉吧!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

android:layout_height=“100dp”
android:layout_gravity=“center”
android:background=“@color/red” />

上面三段代码展示效果如下:

场景①和②:

Screenshot_2021-04-20-14-53-08-964_com.hyy.scroll.jpg

场景③:

Screenshot_2021-04-20-14-54-14-852_com.hyy.scroll.jpg

简单描述下上面出现的情况:

  • 当view的宽高设置成match_parentwrap_content的时候,显示的大小和父view的大小一致
  • 当view的宽高设置成固定值(xxdp)的时候,显示的大小就是我们设置的固定值大小

如果你已经对上面结果感觉毫无疑问而且心里已经知其所以然了,后面的内容可以不用看了。点个赞就可以走了。。。

等等。。开个玩笑。。怎么就当真了呢。。哈哈哈。。废话不多说,走,我们点进去源码瞅瞅。

View.onMeasure

//View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {

//1
onMeasure(widthMeasureSpec, heightMeasureSpec);

}

上述代码①处代码调用了view的onMeasure方法,先不聊widthMeasureSpec和heightMeasureSpec的由来,这个后面补上。接着我们看View的onMeasure方法:

//View.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//1
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),//4
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)//4
);
}

//2
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {

setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

//3
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;

mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

//返回background(Drawable)的minWidth、mMinWidth(android_minWitdh)较大值
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth,mBackground.getMinimumWidth());
}

//返回background(Drawable)的minHeight、mMinHeight(android_minHeight)较大值
protected int getSuggestedMinimumHeight() {
return (mBackground == null) ? mMinHeight :
max(mMinHeight, mBackground.getMinimumHeight());

}

//4
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

switch (specMode) {
//5
case MeasureSpec.UNSPECIFIED:
result = size;
break;
//6
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}

上述代码略微多一点,但是方法都比较简单,下面我们一一介绍下:

  • 标注①②③处的调用链:onMeasure–>setMeasuredDimension–>setMeasuredDimensionRaw 走到方法③setMeasuredDimensionRaw处,view的测量流程就结束了,同时赋值了mMeasuredWidthmMeasuredHeight,这个时候我们就可以通过getMeasuredWidth()getMeasuredHeight() 拿到宽高值了
  • 方法②setMeasuredDimension处又调用了方法④getDefaultSize
  • 方法④getDefaultSize里面大致逻辑是:
  • 如果传入的measureSpecMeasureSpec.Mode的值是MeasureSpec.AT_MOST或者MeasureSpec.EXACTLY 则返回specSize。
  • 如果传入的measureSpecMeasureSpec.Mode的值是MeasureSpec.UNSPECIFIED 则返回size(getSuggestedMinimumWidth()、getSuggestedMinimumHeight())。

现在我们的疑惑点就在于方法①onMeasure的参数是从哪里来的

那么下面我们就瞅瞅FrameLayout相关测量源码

FrameLayout的onMeasure源码解析

在讲FrameLayout的onMeasure前,我们先默认一个事实,这个FrameLayout是Activity的根View,它会被添加到DecorView上,DecorView实际上也是一个FramLayout,下面的源码解释了DecorView的MeasureSpec的由来:

//ViewRootImpl.java

private void performTraversals() {

//获取DecorView的MeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
//测量入口
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);


//layout入口
performLayout(lp, mWidth, mHeight);

//draw入口
performDraw();

}

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {

case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can’t resize. Force root view to be windowSize.
//默认DecorView的measureSpec是:size=windowSize mode是EXACTLY
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension,MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}

//mView即为DecorView
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

从上面的代码得知:DecorView的measureSpec是size=windowSize比如(1920px 1080px) mode=EXACTLY, 然后由于Activity根FramLayout的widthheight都是match_parent, 所以 FrameLayout measureSpec和DecorView的measureSpec是一致的, 接下来我们看FrameLayout的onMeasure源码:

//FrameLayout.java

//这里的widthMeasureSpec的size 手机屏幕宽度 mode是EXACTLY
//heightMeasureSpec的size是手机屏幕高度-状态栏高度 mode是EXACTLY
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//1
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);

}
}


//2
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
}

//ViewGroup.java
//3
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec,
int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

//4
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin

  • widthUsed, lp.width);
    //5
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
    mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
  • heightUsed, lp.height);

//6
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

}

//7
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//measureSpec的mode
int specMode = MeasureSpec.getMode(spec);
//measureSpec的size
int specSize = MeasureSpec.getSize(spec);

int size = Math.max(0, specSize - padding);

int resultSize = 0;
int resultMode = 0;

switch (specMode) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {//child的宽高值是固定值比如40dp
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//child的lp.width是match_parent
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//child的lp.width是wrap_content
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

case MeasureSpec.AT_MOST:
if (childDimension >= 0) {//child的宽高值是固定值比如40dp
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//此时 child的match_parent 和 wrap_content 返回的MeasureSpec 一模一样
//mode都是AT_MOST
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;

case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
//child的宽高值是固定值比如40dp
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//此时 child的match_parent 和 wrap_content 返回的MeasureSpec 一模一样
//mode都是AT_MOST
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}

return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

最后说一下我的学习路线

其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:

  1. 架构师筑基必备技能
  2. Android框架体系架构(高级UI+FrameWork源码)
  3. 360°Androidapp全方位性能调优
  4. 设计思想解读开源框架
  5. NDK模块开发
  6. 移动架构师专题项目实战环节
  7. 移动架构师不可不学习微信小程序
  8. 混合开发的flutter

Android学习的资料

我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。

330页PDF Android学习核心笔记(内含上面8大板块)

Android学习的系统对应视频

总结

我希望通过我自己的学习方法来帮助大家去提升技术:

  • 1、多看书、看源码和做项目,平时多种总结

  • 2、不能停留在一些基本api的使用上,应该往更深层次的方向去研究,比如activity、view的内部运行机制,比如Android内存优化,比如aidl,比如JNI等,并不仅仅停留在会用,而要通过阅读源码,理解其实现原理

  • 3、同时对架构是有一定要求的,架构是抽象的,但是设计模式是具体的,所以一定要加强下设计模式的学习

  • 4、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!

希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

、android的方向也很多,高级UI,移动架构师,数据结构与算法和音视频FFMpeg解码,如果你对其中一项比较感兴趣,就大胆的进阶吧!

希望大家多多点赞,转发,评论加关注,你们的支持就是我继续下去的动力!加油!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值