[Android学习笔记]View的measure过程学习
View从创建到显示到屏幕需要经历几个过程:
measure -> layout -> draw
measure过程:计算view所占屏幕大小
layout过程:设置view在屏幕的位置
draw过程:绘制view
继承自view的控件的measure过程
view.measure(int,int)方法有什么作用?

view.measure(int,int)用于询问(或称为设置)当前view需要(想要)占用多大得空间。
简单理解为,为view申请两个int值大小的尺寸的控件
View.java
/***
* <p>
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
* </p>
*
* <p>
* The actual mesurement work of a view is performed in
* {@link #onMeasure(int, int)}, called by this method. Therefore, only
* {@link #onMeasure(int, int)} can and must be overriden by subclasses.
* </p>
*
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the
* parent
* @param heightMeasureSpec Vertical space requirements as imposed by the
* parent
*
* @see #onMeasure(int, int)
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET; if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
} // measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
} mPrivateFlags |= LAYOUT_REQUIRED;
} mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
从源码看,我们需要关注的几个方法有:
onMeasure(int,int); //如有需求,需重写
setMeasuredDimension(int,int);//保存结果
getDefaultSize(int,int); //原逻辑
getSuggestMinimumHeight(); //原逻辑
getSuggestMinimumWidth(); //原逻辑
MeasureSpec类
MeasureSpec.getMode();
MeasureSpec.getSize();
MeasureSpec.UNSPECIFIED
MeasureSpec.AT_MOST
MeasureSpec.EXACTLY
回过头说,什么时候会用到view.measure(int,int)?
通常情况下,很少显式调用view.measure(int,int)方法,除非是需要根据需求变更view的大小和位置
更多调用view.measure(int,int)方法的是android框架本身,当绘制创建控件时候,android框架会调用此方法
询问view需要多大的空间。
说到这,可以知道两个参数widthMeasureSpec,heightMeasureSpec的作用了。
measure方法中的两个参数是父类传递过来给当前view的一个建议值,即想把当前view的尺寸
设置为宽widthMeasureSpec,高heightMeasureSpec
回到View源码中可以看到,在调用measure(int,int)之后,如果与old值不相等则会回调view的onMeasure(int,int)方法
进行具体实质性的view大小的计算.
(所以,如果你想对自己的view进行一些定制,则需要重写view的onMeasure(int,int)方法,把定制代码写在此方法中)
(注意:从measure源码可以得知,重写onMeasure方法时候记得要调用setMeasuredDimension(int,int),否则在measure中会抛出IllegalStateException异常)
/***
* <p>
* Measure the view and its content to determine the measured width and the
* measured height. This method is invoked by {@link #measure(int, int)} and
* should be overriden by subclasses to provide accurate and efficient
* measurement of their contents.
* </p>
*
* <p>
* <strong>CONTRACT:</strong> When overriding this method, you
* <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
* measured width and height of this view. Failure to do so will trigger an
* <code>IllegalStateException</code>, thrown by
* {@link #measure(int, int)}. Calling the superclass'
* {@link #onMeasure(int, int)} is a valid use.
* </p>
*
* <p>
* The base class implementation of measure defaults to the background size,
* unless a larger size is allowed by the MeasureSpec. Subclasses should
* override {@link #onMeasure(int, int)} to provide better measurements of
* their content.
* </p>
*
* <p>
* If this method is overridden, it is the subclass's responsibility to make
* sure the measured height and width are at least the view's minimum height
* and width ({@link #getSuggestedMinimumHeight()} and
* {@link #getSuggestedMinimumWidth()}).
* </p>
*
* @param widthMeasureSpec horizontal space requirements as imposed by the parent.
* The requirements are encoded with
* {@link android.view.View.MeasureSpec}.
* @param heightMeasureSpec vertical space requirements as imposed by the parent.
* The requirements are encoded with
* {@link android.view.View.MeasureSpec}.
*
* @see #getMeasuredWidth()
* @see #getMeasuredHeight()
* @see #setMeasuredDimension(int, int)
* @see #getSuggestedMinimumHeight()
* @see #getSuggestedMinimumWidth()
* @see android.view.View.MeasureSpec#getMode(int)
* @see android.view.View.MeasureSpec#getSize(int)
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
其中涉及到三个方法:
setMeasuredDimension(int,int)
getDefaultSize(int,int) //原实现
getSuggestedMinimumHeight();//原实现
getSuggestedMinimumWidth(); //原实现
setMeasuredDimension:
/***
* <p>
* Measure the view and its content to determine the measured width and the
* measured height. This method is invoked by {@link #measure(int, int)} and
* should be overriden by subclasses to provide accurate and efficient
* measurement of their contents.
* </p>
*
* <p>
* <strong>CONTRACT:</strong> When overriding this method, you
* <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
* measured width and height of this view. Failure to do so will trigger an
* <code>IllegalStateException</code>, thrown by
* {@link #measure(int, int)}. Calling the superclass'
* {@link #onMeasure(int, int)} is a valid use.
* </p>
*
* <p>
* The base class implementation of measure defaults to the background size,
* unless a larger size is allowed by the MeasureSpec. Subclasses should
* override {@link #onMeasure(int, int)} to provide better measurements of
* their content.
* </p>
*
* <p>
* If this method is overridden, it is the subclass's responsibility to make
* sure the measured height and width are at least the view's minimum height
* and width ({@link #getSuggestedMinimumHeight()} and
* {@link #getSuggestedMinimumWidth()}).
* </p>
*
* @param widthMeasureSpec horizontal space requirements as imposed by the parent.
* The requirements are encoded with
* {@link android.view.View.MeasureSpec}.
* @param heightMeasureSpec vertical space requirements as imposed by the parent.
* The requirements are encoded with
* {@link android.view.View.MeasureSpec}.
*
* @see #getMeasuredWidth()
* @see #getMeasuredHeight()
* @see #setMeasuredDimension(int, int)
* @see #getSuggestedMinimumHeight()
* @see #getSuggestedMinimumWidth()
* @see android.view.View.MeasureSpec#getMode(int)
* @see android.view.View.MeasureSpec#getSize(int)
*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
getDefaultSize:
/***
* Utility to return a default size. Uses the supplied size if the
* MeasureSpec imposed no contraints. Will get larger if allowed
* by the MeasureSpec.
*
* @param size Default size for this view
* @param measureSpec Constraints imposed by the parent
* @return The size this view should be.
*/
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
getSuggestedMinimumHeight:
/***
* Returns the suggested minimum height that the view should use. This
* returns the maximum of the view's minimum height
* and the background's minimum height
* ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).
* <p>
* When being used in {@link #onMeasure(int, int)}, the caller should still
* ensure the returned height is within the requirements of the parent.
*
* @return The suggested minimum height of the view.
*/
protected int getSuggestedMinimumHeight() {
int suggestedMinHeight = mMinHeight; if (mBGDrawable != null) {
final int bgMinHeight = mBGDrawable.getMinimumHeight();
if (suggestedMinHeight < bgMinHeight) {
suggestedMinHeight = bgMinHeight;
}
} return suggestedMinHeight;
}
getSuggestedMinimumWidth:
/***
* Returns the suggested minimum width that the view should use. This
* returns the maximum of the view's minimum width)
* and the background's minimum width
* ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
* <p>
* When being used in {@link #onMeasure(int, int)}, the caller should still
* ensure the returned width is within the requirements of the parent.
*
* @return The suggested minimum width of the view.
*/
protected int getSuggestedMinimumWidth() {
int suggestedMinWidth = mMinWidth; if (mBGDrawable != null) {
final int bgMinWidth = mBGDrawable.getMinimumWidth();
if (suggestedMinWidth < bgMinWidth) {
suggestedMinWidth = bgMinWidth;
}
} return suggestedMinWidth;
}
setMeasuredDimension(int,int)方法
保存了传入的建议尺寸
getDefaultSize(int,int)方法
获取一个默认值
getSuggestedMinimumHeight()方法
获取此控件的最小可用值(如果设置了android:minHeight
getSuggestedMinimumWidth()方法
获取此控件的最小可用值(如果设置了android:minWidth
View源码中可以看到,关键在于getDefaultSize(int,int)方法它,返回最终传递给setMeasuredDimension(int,int)方法的数据,view就用这两个int值最为view的宽高了
getDefaultSize(int,int)中关键在于MeasureSpec类
MeasureSpec类封装了父ivew传递给子view的布局要求,每个MeasureSpc实例代表宽度和高度要求
MeasureSpec.getMode(int)方法
将根据传入的int测量值获取一个对应的模式:
MeasureSpec.UNSPECIFIED:未指定.即父元素对子元素无任何限制
MeasureSpec.EXACTLY:表示确定大小.即父元素决定子元素的确切大小
MeasureSpec.AT_MOST:至多.即子元素最多能到达的大小
(这三个模式与match_parent,wrap_parent的关系十分紧密,之后详细研究)
MeasureSpec.getSize(int)
根据传入的int测量值获取一个int值表达控件的大小
MeasureSpec.makeMeasureSpec(int size,int mode)方法
根据提供的大小值和模式,创建一个测量值
android原实现中getDefaultSize(int,int)可以看到
如果模式为MeasureSpec.UNSPECIFIED,最终大小就是我们申请的大小,如果模式为
MeasureSpec.AT_MOST或者case MeasureSpec.EXACTLY,则最终结果则为MeasureSpec.getSize(measureSpec)的结果
好了,到此android中view的默认实现过程基本结束,做一个简单总结:
1.父类调用view.measure(int,int),传入测量值,建议值
2.子view回调onMeasure(int,int),计算得到最终view的尺寸大小
3.测量过程结束,进行layout过程
所以,如果你自定义一个控件时,对其尺寸有特殊要求,则重写view的onMeasure(int.int)方法对尺寸进行定制即可
(算完之后记得调用setMeasuredDimension(int,int)方法保存计算结果和设置标志,否则抛出异常)
最后:
以上都是个人理解,难免错漏,如需深入研究请转至大神博客:
http://blog.csdn.net/qinjuning/article/details/8074262
[Android学习笔记]View的measure过程学习的更多相关文章
- [Android学习笔记]View的draw过程学习
View从创建到显示到屏幕需要经历几个过程: measure -> layout -> draw measure过程:计算view所占屏幕大小layout过程:设置view在屏幕的位置dr ...
- [Android学习笔记]view的layout过程学习
View从创建到显示到屏幕需要经历几个过程: measure -> layout -> draw measure过程:计算view所占屏幕大小layout过程:设置view在屏幕的位置dr ...
- Android学习笔记View的工作原理
自定义View,也可以称为自定义控件,通过自定义View可以使得控件实现各种定制的效果. 实现自定义View,需要掌握View的底层工作原理,比如View的测量过程.布局流程以及绘制流程,除此之外,还 ...
- openstack学习笔记一 虚拟机启动过程代码跟踪
openstack学习笔记一 虚拟机启动过程代码跟踪 本文主要通过对虚拟机创建过程的代码跟踪.观察虚拟机启动任务状态的变化,来透彻理解openstack各组件之间的作用过程. 当从horizon界面发 ...
- 【Android - 自定义View】之View的measure过程解析
measure(测量)过程是View的工作流程中最开始.最核心的过程,在这个过程中负责确定View的测量宽/高. 对于View和ViewGroup,measure过程有不同的执行方法:如果目标是一个原 ...
- 《Java学习笔记(第8版)》学习指导
<Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...
- 20145213《Java程序设计学习笔记》第六周学习总结
20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...
- 20145230《java学习笔记》第七周学习总结
20145230 <Java程序设计>第7周学习总结 教材学习内容 Lambda语法概览 我们在许多地方都会有按字符串长度排序的需求,如果在同一个方法内,我们可以使用一个byName局部变 ...
- 【学习笔记】JavaScript的基础学习
[学习笔记]JavaScript的基础学习 一 变量 1 变量命名规则 Camel 标记法 首字母是小写的,接下来的字母都以大写字符开头.例如: var myTestValue = 0, mySeco ...
随机推荐
- 基于visual Studio2013解决C语言竞赛题之1007找数
题目 解决代码及点评 /************************************************************************/ ...
- 深入理解mysql之BDB系列(1)---BDB相关基础知识
深入理解mysql之BDB系列(1) ---BDB相关基础知识 作者:杨万富 一:BDB体系结构 1.1.BDB体系结构 BDB总体的体系结构如图1.1所看到的,包括五个子系统(见图1.1 ...
- [代码示例]用Fine Uploader+ASP.NET MVC实现ajax文件上传
原文 [代码示例]用Fine Uploader+ASP.NET MVC实现ajax文件上传 Fine Uploader(http://fineuploader.com/)是一个实现 ajax 上传文件 ...
- 互联网组织的未来:剖析GitHub员工的任性之源(转)
如果有这么家任性的公司,没有所谓“经理人”这一层,人都在做自己喜欢的事情,并且创造价值,而其他的事情,就顺其自然让他发生.这里能节省多少官僚主义带来的浪费?这样的公司得跑得有多快?得有多少无谓的冲突消 ...
- attachEvent和addEventListener详解
attachEvent方法可以动态的为网页内的元素添加一个事件.通常你想为某个按扭添加一个单击事件时.你都会在按扭内写上onclick=事件名称.使用attachEvent则不必这样做.你把写好的事件 ...
- Swift - 使用NSURLSession加载数据、下载、上传文件
NSURLSession类支持三种类型的任务:加载数据.下载和上传.下面通过样例分别进行介绍. 1,使用Data Task加载数据 使用全局的sharedSession()和dataTaskWithR ...
- OnClick事件的Sender参数的前世今生——TWinControl.WinProc优先捕捉到鼠标消息,然后使用IsControlMouseMsg函数进行消息转发给图形子控件(意外发现OnClick是由WM_LBUTTONUP触发的)
这是一个再普通不过的Button1Click执行体: procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage('I am B ...
- AngelHack China 2013 招组队成员
AngelHack China 2013 connect me
- 正则表达式概述与JAVA中正则表达式的应用
编程或者电脑使用过程中,经常需要对字符串进行 匹配,查找,替换,判断.如果单纯用代码 if () ,whlie 什么的进行比较复杂麻烦.正则表达式是一种强大灵活的文本处理工具,专门对字符串进行匹配,查 ...
- 一个能够自己主动生成静态库,自己主动安装程序的Makefile
.PHONY:clean install CC=g++ CFLAGS=-Wall -g BIN=libecho.a INCLUDE=echo SRC=src OBJS=Socket.o Rio.o T ...