MeasureSpec
测量规格,封装了父容器对 view 的布局上的限制,内部提供了宽高的信息(SpecMode、SpecSize),SpecSize是指在某种SpecMode下的参考尺寸,其中SpecMode 有如下三种:
-
UNSPECIFIED 父控件不对你有任何限制,你想要多大给你多大,想上天就上天。这种情况一般用于系统内部,表示一种测量状态。(这个模式主要用于系统内部多次Measure的情形,并不是真的说你想要多大最后就真有多大)
-
EXACTLY 父控件已经知道你所需的精确大小,你的最终大小应该就是这么大。
-
AT_MOST 你的大小不能大于父控件给你指定的size,但具体是多少,得看你自己的实现。
/** *
-
目标是将父控件的测量规格和child view的布局参数LayoutParams相结合,得到一个
-
最可能符合条件的child view的测量规格。
-
@param spec 父控件的测量规格
-
@param padding 父控件里已经占用的大小
-
@param childDimension child view布局LayoutParams里的尺寸
-
@return child view 的测量规格 */ 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) { // 当父控件的测量模式 是 精确模式. case MeasureSpec.EXACTLY: //如果child的布局参数有固定值,比如"layout_width" = "100dp" //那么显然child的测量规格也可以确定下来了,测量大小就是100dp,测量模式也是EXACTLY if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; }
//如果child的布局参数是"match_parent",也就是想要占满父控件 //而此时父控件是精确模式,也就是能确定自己的尺寸了,那child也能确定自己大小了 else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.EXACTLY; } //如果child的布局参数是"wrap_content",也就是想要根据自己的逻辑决定自己大小, //比如TextView根据设置的字符串大小来决定自己的大小 //那就自己决定呗,不过你的大小肯定不能大于父控件的大小嘛 //所以测量模式就是AT_MOST,测量大小就是父控件的size else if (childDimension == LayoutParams.WRAP_CONTENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } break;// 当父控件的测量模式 是 最大模式,也就是说父控件自己还不知道自己的尺寸,但是大小不能超过size case MeasureSpec.AT_MOST: //同样的,既然child能确定自己大小,尽管父控件自己还不知道自己大小,也优先满足孩子的需求 if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } //child想要和父控件一样大,但父控件自己也不确定自己大小,所以child也无法确定自己大小 //但同样的,child的尺寸上限也是父控件的尺寸上限size else if (childDimension == LayoutParams.MATCH_PARENT) { resultSize = size; resultMode = MeasureSpec.AT_MOST; } //child想要根据自己逻辑决定大小,那就自己决定呗 else if (childDimension == LayoutParams.WRAP_CONTENT) { 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); }
-
基本概念以及流程 可以看到,官方的文档只是泛泛的说了一下,没有具体详细的说明整个流程,然后在网上找了一些资料,结合官方文档来详细说了下,对于理解这个部分,效果可能会更好。在正式介绍之前,还是需要理解与UI相关的基本概念。 Activity:Activity包含一个Window,该Window在Activity的attach方法中通过调用PolicyManager.makeNewWindo创建; View:最基本的UI组件,表示屏幕上的一个矩形区域;
DecorView:是Window中View的RootView,设置窗口属性;
Window:表示顶层窗口,管理界面的显示和事件的响应;每个Activity 均会创建一个 PhoneWindow对象,是Activity和整个View系统交互的接口
WindowManager:一个interface,继承自ViewManager。所在应用进程的窗口管理器;有一个implementation WindowManagerImpl;主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。
ViewRoot:通过IWindowSession接口与全局窗口管理器进行交互:界面控制和消息响应;
ActivityThread:应用程序的主线程,其中会创建关联当前Activity与Window;创建WIndowManager实现类实例,把当前DecoView加入到WindowManager;


