View MeasureSpec 和LayoutParams关系
来源:互联网 发布:移动硬盘 知乎 编辑:程序博客网 时间:2024/04/30 12:48
View MeasureSpec 和LayoutParams关系
系统内部是通过MeasureSpec来给View 进行测量工作的,但是我们实际却是只用LayoutParams来设置的.这里我们就是分析2者直接的联系.其实View在测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后根据这个MeasureSpec来确定View测量之后的高和宽.也就是说MeasureSpec不是由LayoutParams唯一确定的,而是要和父容器一起来确定的.而且普通的View和页面的顶级View(DecorView)的MeasureSpec的转换过程是有些不同的.
DecorView的MeasureSpec是由窗口的尺寸和DecorView其自身的LayoutParams来共同决定的.
普通View的MeasureSpec是由父容器的MeasureSpec和普通View自身的LayoutParams来共同决定的.
在DecorView的measureHierarchy方法就是它的MeasureSpec创建的过程.
ViewRootImpl.java(这里删除了部分log和暂时不用关注的代码)
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,
final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
int childWidthMeasureSpec;
int childHeightMeasureSpec;
boolean windowSizeMayChange = false;
boolean goodMeasure = false;
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {//这个情况是弹窗,普通的页面窗口不是wrap content
//..........
}
if (!goodMeasure) {//
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
windowSizeMayChange = true;
}
}
return windowSizeMayChange;
}
上面的代码中childHeightMeasureSpec和childWidthMeasureSpec就是屏幕的尺寸lp就是DecorView的参数.所以我们需要现在看看getRootMeasureSpec方法
ViewRootImpl.java
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.
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;
}
根据上面的getRootMeasureSpec()可以知道:
如果DecorView的LayoutParams是match parent模式,窗口的大小就是DecorView的大小.
如果DecorView的LayoutParams是wrap content模式,DecorView就是最大状态,但是具体大小没有确定(不能超过窗口大小)
如果DecorView的LayoutParams是具体的大小,比如多少像素之类的,那么DecorView 就是它LayoutParams中的实际大小.
同时, DecorView的LayoutParams是match parent或者具体数值(dp,px)的时候,其直接的子View会是精确模式(EXACTLY),
DecorView的LayoutParams是warp content的时候,其直接的子View会是最大模式(AT_MOST)
在measureHierarchy获取DecorView的MeasureSpec之后就是通过performMeasure()来执行
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec) ,这样就是调用DecorView的自己的measure()方法.关于measure()方法这里暂时不分析.
对普通的View来说,它的measure过程是由它的ViewGroup在measureChildWithMargins()来调用的
ViewGroup.java
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
对ViewGroup的每个child 都会被执行上面的measureChildWithMargins方法,先使用父容器ViewGroup的MeasureSpec和child view 自己的LayoutParams来获取child view自己的MeasureSpec,在调用child view自己本身的measure().
measure()方法后续分析.
上面的是使用getChildMeasureSpec来获取child view 的MeasureSpec的,下面我们分析一下这个方法.
ViewGroup.java
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);//父容器可用的大小
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {//view 是固定宽高
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {//view 是match parent
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//view 是wrap content// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
//view 是固定宽高// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//view 是match parent// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//view 是wrap content// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
//主要用于系统内部多次Measure,暂时不关注if (childDimension >= 0) {
//view 是固定宽高// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//view 是match parent// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//view 是wrap content// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
所以我们可以知道,对于普通的View,它的MeasureSpec有父容器的MeasureSpec和View自身的LayoutParams来一起决定的,那么不然的父容器的MeasureSpec和不同View 的LayoutParams,View 就可以有很多种不同的MeasureSpec.当是根据以上代码可以总结如下:
1.当View 是固定宽高的时候,不管父容器是什么模式,View都是精确模式(EXACTLY),并遵循使用LayoutParams里面的大小.
2.当View 的宽高是match parent,父容器是精确模式(EXACTLY),View也是精确模式(EXACTLY);父容器是最大模式(AT_MOST),View也是最大模式(AT_MOST).而且这2种情况View 的大小都是父容器剩余大小空间
3.当View 的宽高是wrap content,不管父容器是精确模式(EXACTLY)还是最大模式(AT_MOST),View始终都是最大模式(AT_MOST).并且大小都是父容器剩余的大小空间
注意:上面提到的精确模式和最大模式已经大小都在MeasureSpec里面有体现,可以参考其他笔记.
1 0
- View MeasureSpec 和LayoutParams关系
- [看书日记20151226]MeasureSpec和LayoutParams的对应关系 , View的工作流程
- MeasureSpec和LayoutParams
- 【View工作原理】ViewRoot、DecorView、MeasureSpec和LayoutParams
- View—DecorView,measureSpec与LayoutParams
- 浅析ViewGroup中的MeasureSpec和LayoutParams
- View.MeasureSpec
- ViewGroup和LayoutParams之间的关系
- 关于 View.measure 和 MeasureSpec 一下资料整理
- 自定义view的measureSpec是谁的mode和size
- View的MeasureSpec使用
- 【view】MeasureSpec介绍
- 解析View中的MeasureSpec
- 【Android基础】-View.MeasureSpec
- Android View MeasureSpec详解
- WindowManager.LayoutParams() 关于重心和坐标和的关系
- WindowManager.LayoutParams//android.view
- Android View.ViewGroup.LayoutParams
- iOS 写入日历
- 人生如茶
- Spirng IoC/DI容器 的helloworld工程2-多个配置文件
- Java中的多线程你只要看这一篇就够了
- 交流中相谈甚欢
- View MeasureSpec 和LayoutParams关系
- nginx 下载地址
- JAVA高级【3.4】《Java核心技术2》网络-webscket例子汇总
- 中间件
- View-MeasuerSepc
- 5 localhost:8080/portal改为wangxin:8081/hahaha
- <Android开源库> EventBus 用法详解<译文>
- 微信公众帐号开发教程第6篇-文本消息的内容长度限制揭秘
- 图的基本存储的基本方式一 邻接矩阵