解析View中的MeasureSpec
来源:互联网 发布:淘宝做外宣技巧 编辑:程序博客网 时间:2024/05/18 02:01
MeasureSpec是View中的一个内部类,封装了父View传给子View的布局需求:
public static class MeasureSpec { private static final int MODE_SHIFT = 30; private static final int MODE_MASK = 0x3 << MODE_SHIFT; public static final int UNSPECIFIED = 0 << MODE_SHIFT; public static final int EXACTLY = 1 << MODE_SHIFT; public static final int AT_MOST = 2 << MODE_SHIFT; public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } } public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); } static int adjust(int measureSpec, int delta) { final int mode = getMode(measureSpec); if (mode == UNSPECIFIED) { // No need to adjust size for UNSPECIFIED mode. return makeMeasureSpec(0, UNSPECIFIED); } int size = getSize(measureSpec) + delta; if (size < 0) { Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size + ") spec: " + toString(measureSpec) + " delta: " + delta); size = 0; } return makeMeasureSpec(size, mode); } public static String toString(int measureSpec) { int mode = getMode(measureSpec); int size = getSize(measureSpec); StringBuilder sb = new StringBuilder("MeasureSpec: "); if (mode == UNSPECIFIED) sb.append("UNSPECIFIED "); else if (mode == EXACTLY) sb.append("EXACTLY "); else if (mode == AT_MOST) sb.append("AT_MOST "); else sb.append(mode).append(" "); sb.append(size); return sb.toString(); } }
MeasureSpec定义了三种模式:
UNSPECIFIED:父View对子View没有限制,子View想多大就多大。一般这种模式很少用。
EXACTLY:父View可以确定子View的大小。
AT_MOST:子View不能超过父View限定的最大值。
在View的measure方法里面,会接收两个int参数:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... }
这里面传入的值,不仅仅是代表View的size,还包含了MeasureSpec中的mode。在调用View的measure方法之前,必须先计算出View宽度的MeasureSpec和高度的MeasureSpec,最先调用measure方法的是DecorView,看看它是怎么计算MeasureSpec的吧:
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; }
从上面的代码可以看到,根据不同的LayoutParams,调用makeMeasureSpec时传入不同的MeasureSpec mode,这也很好理解,LayoutParams决定者View的大小。关键就是makeMeasureSpec方法了:
public static int makeMeasureSpec(int size, int mode) { if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } }
这个方法很简单,但是理解起来并不容易。先把MeasureSpec 定义的几个常量用16进制表示:
0x3左移30位: MODE_MASK = 11 00 0000 0000 0000 0000 0000 0000 000000 左移30位: UNSPECIFIED = 00 00 0000 0000 0000 0000 0000 0000 000001 左移30位:EXACTLY = 01 00 0000 0000 0000 0000 0000 0000 000010 左移30位:AT_MOST = 10 00 0000 0000 0000 0000 0000 0000 0000MODE_MASK取反:~MODE_MASK= 00 11 1111 1111 1111 1111 1111 1111 1111
size & ~MODE_MASK就是取size 的后30位,mode & MODE_MASK就是取mode的前两位,最后执行或运算,得出来的数字,前面2位包含代表mode,后面30位代表size。在measure方法里面需要取出size或者是mode时,可以调用下面两个方法:
public static int getMode(int measureSpec) { return (measureSpec & MODE_MASK); } public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); }
这样做其实就是用一个int存储了两个信息,好处是节省内存开支,减少传参的个数,View源码中很多用这种方法来存储信息,比如View 的VISIBLE,ENABLED等等,这些View的状态,都是存储在int 变量mViewFlags之中,它里面不同的位数代表着不同的状态。
在View的measure过程中,我们知道子View的MeasureSpec和父View的MeasureSpec有有关联,计算子View的MeasureSpec是调用ViewGroup中的getChildMeasureSpec来实现的:
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) { resultSize = childDimension; resultMode = MeasureSpec.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 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.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和子View size之间的联系:
- 解析View中的MeasureSpec
- Android源码分析之---View.MeasureSpec 解析
- View.MeasureSpec
- MeasureSpec解析
- View的MeasureSpec使用
- 【view】MeasureSpec介绍
- 【Android基础】-View.MeasureSpec
- Android View MeasureSpec详解
- MeasureSpec类解析
- Android View(三)-MeasureSpec详解
- 自定义View准备:MeasureSpec理解
- View中MeasureSpec的作用
- View MeasureSpec 和LayoutParams关系
- Android API之android.view.View.MeasureSpec
- Android中自定义View的MeasureSpec使用
- Android中自定义View的MeasureSpec使用
- Android中自定义View的MeasureSpec使用
- Android中自定义View的MeasureSpec使用
- L4:二维数组、数组类、数组值复制、冒泡排序、二分查找
- Same Tree
- 初学iOS开发C语言基础
- SSIS数据流性能比较(ADO.NET vs. OLE DB vs. ODBC)
- web.xml 详细介绍
- 解析View中的MeasureSpec
- Linux进程间通信——使用信号量
- Linux进程间通信——使用命名管道
- 2015年ALPC暑期专题练习I (计算几何) D Intersecting Lines
- Minimum Depth of Binary Tree
- HDU3363 Ice-sugar Gourd
- 【动效设计】常见动效设计分门别类
- Linux进程间通信——使用匿名管道
- C#高级编程四十二天----委托和事件