Android UI 绘制过程浅析(二)onMeasure过程
前言
View的绘制过程分为 measure、layout、draw三个步骤,接下来对这三个步骤逐一进行研究。
measure方法的签名
public final void measure(int widthMeasureSpec, int heightMeasureSpec);
measure方法用来测量View的尺寸。两个参数widthMeasureSpec/heightMeasureSpec声明了Parent View所能提供的宽/高。
需要注意的是,widthMeasureSpec/heightMeasureSpec 这两个 int 型的参数是复合参数(compound parameters)。jvm中以32bit来存储int,最高两位表示“mode”,后面30位才是具体的数值。在View.java中有下面两个方法
/**
* Extracts the mode from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the mode from
* @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
* {@link android.view.View.MeasureSpec#AT_MOST} or
* {@link android.view.View.MeasureSpec#EXACTLY}
*/
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
} /**
* Extracts the size from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the size from
* @return the size in pixels defined in the supplied measure specification
*/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
看到了吧,使用位操作&来获取最高两位的mode。2^2=4,这里只有用到了3种,它们分别的含义入下
- UNSPECIFIED-ParentView对子View没有约束,后者可以是任意大小
- AT_MOST-ParentView规定了尺寸上限,子View的大小不可以超过这个上限
- EXACTLY-ParentView规定了子View的宽高,子View只能是这个宽高
measure方法本身是声明为final的,CustomView必须实现自己的onMeasure方法,以便View.measure对此进行回调。在实现自己的CustomView.onMeasure之前,先来看看View中默认的onMeasure方法。
onMeasure
在自定义View中,往往需要重写onMeasure方法,来设置View的measured width/height。要注意的一点是,计算完成后,必须调用setMeasuredDimension(int, int)来设置宽高,不然在上层measure方法会抛出IllegalStateException。自定View计算出的宽/高必须满足最小宽/高(通过getSuggestedMinimumWidth()、getSuggestedMinimumHeight()获取)。当然也可以在自定义View的onMeasure方法中直接调用View.onMeasure,如下
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
然而,View默认的onMeasure实现逻辑非常简单,恐怕不能满足自定义View的需求。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
View.onMeasure简单粗暴地调用了setMeasuredDimension来设置measuredWidth/measuredHeight。其中使用的getDefaultSize(int, int)方法如下
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;
}
逻辑很简单,一个switch/case语句,根据SpecMode来判断返回。若为UNSPECIFIED,则返回minimumWidth;若为AT_MOST/EXACTLY,返回SpecWidth。
到此为止,View的measure过程已经完成了。对于ViewGroup,可以猜想,它的onMeasure方法,是把所有子View的onMeasure做一次相加。我们来看一下具体是不是这样做的。
ViewGroup.measure
ViewGroup继承了View类,理所应当地,我们试图在ViewGroup中查找是否重写了onMeasure方法——未果。不过我们惊喜地发现了measureChildren这个方法——用来遍历所有children,对每一个child进行measure
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
measureChild中,将ViewGroup的padding加入了计算,最后调用child.measure,这里不予深究,也不再贴出代码。
ViewGroup.java本身并没有重写onMeasure方法,在它的具体实现中,onMeasure根据具体情况,将children的measured size进行求和处理,具体可以参阅FrameLayout/LiearLayout/RelativeLayout 的代码。
小结
对于measure过程的分析就到这里,下一篇我们继续探讨onLayout过程。
Android UI 绘制过程浅析(二)onMeasure过程的更多相关文章
- Android UI 绘制过程浅析(五)自定义View
前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View> ...
- 传感器仿真平台——UI绘制模块(二)
这一章讲的是UI绘制模块 该模块的作用是将实验对象绘制出来,它可能是目标.传感器等等,由于事先并不知道会有哪些物体,也无法事先定义好某个对象该怎么画,以我懒人的性格,得了,就抛给用的人吧~喝前摇一摇, ...
- Android UI 绘制过程浅析(四)draw过程
前言 draw是绘制View三个步骤中的最后一步.同measure.layout一样,通常不对draw本身进行重写,draw内部会调用onDraw方法,子类View需要重写onDraw(Canvas) ...
- Android UI 绘制过程浅析(三)layout过程
前言 上一篇blog中,了解到measure过程对View进行了测量,得到measuredWidth/measuredHeight.对于ViewGroup,则计算出全部children的宽高进行求和. ...
- Android UI 绘制过程浅析(一)LayoutInflater简介
前言 这篇blog是我在阅读过csdn大牛郭霖的<带你一步步深入了解View>一系列文章后,亲身实践并做出的小结.作为有志向的前端开发工程师,怎么可以不搞懂View绘制的基本原理——简直就 ...
- Android UI绘制流程及原理
一.绘制流程源码路径 1.Activity加载ViewRootImpl ActivityThread.handleResumeActivity() --> WindowManagerImpl.a ...
- 简单研究Android View绘制一 测量过程
2015-07-27 16:52:58 一.如何通过继承ViewGroup来实现自定义View?首先得搞清楚Android时如何绘制View的,参考Android官方文档:How Android Dr ...
- Android UI开发第四十一篇——墨迹天气3.0引导界面及动画实现
周末升级了墨迹天气,看着引导界面做的不错,模仿一下,可能与原作者的代码实现不一样,但是实现的效果还是差不多的.先分享一篇以前的文章,android动画的基础知识,<Android UI开发第十二 ...
- 简单研究Android View绘制三 布局过程
2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: /* ...
随机推荐
- HDU 3555 Bomb
RT. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> ...
- OD调试篇11
先看看今天这道菜是用什么语言写的 发现是用VC7.0写的,再随便打开看看 发现未注册以及使用限制,那就用OD载入吧 右击查找所有字符串,找unregistered 找到后双击进入 我们会看见一个 ...
- OD调试篇7--笔记及解题报告
MFC:微软基础类库(英语:Microsoft Foundation Classes,简称MFC)是一个微软公司提供的类库(class libraries),以C++类的形式封装了Windows AP ...
- 从零开始学习Node.js例子四 多页面实现数学运算 续二(client端和server端)
1.server端 支持数学运算的服务器,服务器的返回结果用json对象表示. math-server.js //通过监听3000端口使其作为Math Wizard的后台程序 var math = r ...
- PHP 调试
$log_path = '/data0/www/html/hejungw/static/log/uploadlog/'; $log = $log_path . date('Y-m-d') . '.lo ...
- WindowsPhone开发—— 使用手绘图片做景区导览地图
前些日子在做景区App遇到需求,使用手绘图片做一个简易的地图,支持放大缩小平移以及显示景点Mark,安卓上可以使用一个叫做“mAppWidget”的开源库来完成,WP上有人建议用ArcGIS,但是考虑 ...
- iOS开发之Objective-C与JavaScript的交互(转载)
UIWebView是iOS最常用的SDK之一,它有一个stringByEvaluatingJavaScriptFromString方法可以将javascript嵌入页面中,通过这个方法我们可以在iOS ...
- Jena语义Web开发101
2015/05/28更新 代码在 https://github.com/zhoujiagen/semanticWebTutorialUsingJena 前言 该手册参考和扩展“Hebeler J, F ...
- strncpy基本用法
见百度百科. 注意这句话: (c/c++)复制src中的内容(字符,数字.汉字....)到dest,复制多少由num的值决定,返回指向dest的指针.如果遇到null字符('\0'),且还没有到num ...
- 关于 MAXScript 中文路径返回上级目录(精简版)
之前写过一个 关于 MAXScript 中文路径返回上级目录 的博文 今天无意中发现了一个更简单的方法 代码如下: fn newfile filepath = ( nf = getfilenamepa ...