View在测量时的MeasureSpec由什么决定?
我们都知道系统要确定View的大小,首先得先获得MeasureSpec,再通过MeasureSpec来决定View的大小。
MeasureSpec(32为int值)由两部分组成:
SpecMode(高2位):测量模式。
SpecSize(低30位):某种测量模式下的规格大小。
SpecMode有3类:
UNSPECIFIED: 父容器不对view做大小限制,一般用于系统内部,表示一种测量状态。
EXACTLY:精确模式。对应于:LayoutPrams中的match_parent和具体数值。
AT_MOST:最大值模式。对应于LayoutParam中的wrap_content模式。
那么问题来了,这个MeasureSpec又是由什么决定的呢?我们从源代码里面入手。
ViewGroup里面有一个方法,叫做measureChildWithMargins,用来测量子view的大小的。
/**
* Ask one of the children of this view to measure itself, taking into
* account both the MeasureSpec requirements for this view and its padding
* and margins. The child must have MarginLayoutParams The heavy lifting is
* done in getChildMeasureSpec.
*
* @param child The child to measure
* @param parentWidthMeasureSpec The width requirements for this view
* @param widthUsed Extra space that has been used up by the parent
* horizontally (possibly by other children of the parent)
* @param parentHeightMeasureSpec The height requirements for this view
* @param heightUsed Extra space that has been used up by the parent
* vertically (possibly by other children of the parent)
*/
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);
}
可以看出,在代码里,会先获取childWidthMeasureSpec和childHeightMeasureSpec,然后再测量子元素 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
我们关键是要看这两个MeasureSpec是怎么获取到的,通过方法getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);我们可以看出MeasureSpec的获取不但与子元素本身的LayoutParam有关,还与父容器的MeasureSpec有关,当然,也与它的padding,margin有关。
我们进去看看,这部分代码有点长,不过里面的逻辑很简单,我们直接在源代码里面通过加注释来分析。
/**
* Does the hard part of measureChildren: figuring out the MeasureSpec to
* pass to a particular child. This method figures out the right MeasureSpec
* for one dimension (height or width) of one child view.
*
* The goal is to combine information from our MeasureSpec with the
* LayoutParams of the child to get the best possible results. For example,
* if the this view knows its size (because its MeasureSpec has a mode of
* EXACTLY), and the child has indicated in its LayoutParams that it wants
* to be the same size as the parent, the parent should ask the child to
* layout given an exact size.
*
* @param spec The requirements for this view
* @param padding The padding of this view for the current dimension and
* margins, if applicable
* @param childDimension How big the child wants to be in the current
* dimension
* @return a MeasureSpec integer for the child
*/
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; //子元素的specSize
int resultMode = 0; //子元素的specMode switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY: //如果父容器的测量模式为EXACTLY
if (childDimension >= 0) { //如果子元素的LayoutParam为具体数值>=0
resultSize = childDimension; //那么子元素的specSize就是childDimension
resultMode = MeasureSpec.EXACTLY; //那么子元素的specMode是EXACTLY
//以下的分析都是一样的,也就不写了
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.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) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.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) {
// 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:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
//这里View.sUseZeroUnspecifiedMeasureSpec一直都是false,因此resultSize==0;
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
由以上的分析可以看出子元素的MeasureSpec的获取不但与子元素本身的LayoutParam有关,还与父容器的MeasureSpec有关,当然,也与它的padding,margin有关。
其实我们可以得出下面的结论:
|
parentMeasureSpec childLayoutParam |
EXACTLY |
AT_MOST |
UNSPECIFIED |
|
具体数值 |
EXACTLLY childSize |
EXACTLY childSize |
EXACTLY childSize |
|
Match_parent |
EXACTLY parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
|
Wrap_content |
AT_MOST parentSize |
AT_MOST parentSize |
UNSPECIFIED 0 |
parentSize是父容器的剩余空间。
从上面的表格也可以看出,当我们直接继承view来实现自定义控件的时候,需要重写onMeasure方法并设置wrap_content时候的自身大小,不然使用wrap_content得时候就相当于使用match_parent。我们可以用下面的模板:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode=MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec); int widthSpecSize=MeasureSpec.getSize(widthMeasureSpec);
int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode==MeasureSpec.AT_MOST && heightSpecMode==MeasureSpec.AT_MOST)
{
setMeasuredDimension(100,100);
}
else if (widthSpecMode==MeasureSpec.AT_MOST)
{
setMeasuredDimension(100,heightSpecSize);
}
else if (heightSpecMode==MeasureSpec.AT_MOST)
{
setMeasuredDimension(widthSpecSize,100);
}
}
上面代码中的100是我们自己设置的数值,你也可以用其他数值,根据自己需要。
以上便是对MeasureSpec由何决定的分析。
View在测量时的MeasureSpec由什么决定?的更多相关文章
- 【转载】快速理解android View的测量onMeasure()与MeasureSpec
笔者之前有一篇文章已经使用onMeasure()解决了listview与scollview的显示冲突问题,博客地址如下: onMeasure简单方法 完美解决ListView与ScollView冲突问 ...
- Android view的测量及绘制
讲真,自我感觉,我的水平真的是渣的一匹,好多东西都只停留在知道和会用的阶段,也想去研究原理和底层的实现,可是一看到代码就懵逼了,然后就看不下去了, 说自己不着急都是骗人的,我自己都不信,前两天买了本& ...
- View学习(二)-View的测量(measure)过程
在上一篇文章中,我们介绍了DecorView与MeasureSpec, 下面的文章就开始讨论View的三大流程. View的三大流程都是通过ViewRoot来完成的.ViewRoot对应于ViewRo ...
- 通过源码分析View的测量
要理解View的测量,首先要了解MeasureSpec,系统在测量view的宽高时,要先确定MeasureSpec. MeasureSpec(32为int值)由两部分组成: SpecMode(高2位) ...
- [置顶]
长谈:关于 View Measure 测量机制,让我一次把话说完
<倚天屠龙记中>有这么一处:张三丰示范自创的太极剑演示给张无忌看,然后问他记住招式没有.张无忌说记住了一半.张三丰又慢吞吞使了一遍,问他记住多少,张无忌说只记得几招了.张三丰最后又示范了一 ...
- android View的测量和绘制
本篇内容来源于android 群英传(徐易生著) 我写到这里,是觉得徐易生讲的确实很好, 另外加入了一些自己的理解,便于自己基础的提高. 另外参考:http://www.gcssloop.com/cu ...
- Android View 如何测量
对于Android View的测量,我们一句话总结为:"给我位置和大小,我就知道您长到那里". 为了让大家更好的理解这个结论,我这里先讲一个日常生活中的小故事:不知道大家玩过&qu ...
- IOS中设置cell的背景view和选中时的背景view 、设置cell最右边的指示器(比如箭头\文本标签)
一.Cell的设置 1.设置cell的背景view和选中时的背景view UIImageView *bg = [[UIImageView alloc] init]; bg.image = [UIIma ...
- Android RelativeLayout wrap_content 而且 child view 使用 layout_alignParentBottom 时 RelativeLayout 高度会占满屏幕
Android RelativeLayout wrap_content 而且 child view 使用 layout_alignParentBottom 时 RelativeLayout 高度会占满 ...
随机推荐
- Spring详解(七)------AOP 注解
上一篇博客我们讲解了 AspectJ 框架如何实现 AOP,然后具体的实现方式我们是通过 xml 来进行配置的.xml 方式思路清晰,便于理解,但是书写过于麻烦.这篇博客我们将用 注解 的方式来进行 ...
- jsbridge的js封装
/*注意:源生app需要配置jsbridge的环境,而在前端页面中需要下方封装代码,既可以达到调用app方法的功能和注册供app调用的方法1.注册方法:注册后,供app调用,注册时,同名函数,下一个会 ...
- 剑指offer【03】- 从尾到头打印链表(4种实现方法)
题目:从尾到头打印链表 考点:链表 题目描述:输入一个链表,按链表值从尾到头的顺序返回一个ArrayList. 法一:ArrayList头插法 /** * public class ListNode ...
- 课程五(Sequence Models),第三周(Sequence models & Attention mechanism) —— 1.Programming assignments:Neural Machine Translation with Attention
Neural Machine Translation Welcome to your first programming assignment for this week! You will buil ...
- .Net Core 2.0 preview1实现自定义认证方案
Microsoft.Authentication的使用方法在2.0中发生了比较大的变化,在1.1中认证配置是在Configure中完成. public void ConfigureServices(I ...
- Spring Boot SOAP Webservice例子
前言 本文将学习如何利用Spring boot快速创建SOAP webservice服务: 虽然目前REST和微服务越来越流行,但是SOAP在某些情况下,仍然有它的用武之地: 在本篇 spring b ...
- 怎么样使用CLion调试分析MySQL Server
由于在写MySQL日志订阅服务时候,需要确定在什么event之后保存position,所以就开始研究MySQL的源码,刚开始采用最原始的打印输出的方式去调试,然后每次改完编译运行,效率好低,让我很绝望 ...
- PYTHON 中 SQL 带参数
使用 PYTHON 的字符串填充方式 import mysql.connector sql = 'select \* from school.student where age > {age} ...
- 【原创】深入理解c++的右值引用
0 左值和右值 一个左值表达式代表的是对象本身,而右值表达式代表的是对象的值:变量也是左值. 1 右值引用作用 为了支持移动操作(包括移动构造函数和移动赋值函数),C++才引入了一种新的引 ...
- 闪电侠 Netty 小册里的骚操作
前言 即使这是一本小册,但基于"不提笔不读书"的理念,仍然有必要总结一下.此小册对于那些"硬杠 Netty 源码 却不曾在千万级生产环境上使用实操"的用户非常有 ...