安卓自定义View
知识点:1.ViewRoot和DecorView2.MeasureSpec3.measure layout draw4. 定义View
ViewRoot和DecorView
ViewRoot和DecorView
ViewRoot和DecorView
右图是UI界 的架构图,每个Activity都包含 个Window对象,在Android中通常有PhoneWindow来实现,PhoneWindow将 个DecorView设置为整个应 窗 的根View.DecorView将要显示的内容呈现在PhoneWindow上
ViewRoot和DecorView
ViewRoot是连接WindowManager与DecorView的纽带,View的整个绘制流程的三 步(measure、layout、draw)都是通过ViewRoot完成的。当Activity对象被创建完毕后,会将DecorView添加到Window中(Window是对窗 的抽象,DecorView是 个窗 的顶级容 View,其本质是 个FrameLayout),同时会创建ViewRootImpl(ViewRoot的实现类)对象,并将ViewRootImpl与DecorView建 关联。整个View树的绘图流程是在ViewRoot.Java类的performTraversals()函数展开的,该函数做的执 过程可简单概况为
根据之前设置的状态,判断是否需要重新计算视图 (measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘(draw),其框架过程如下:
整个View树的绘图流程是在ViewRoot.Java类的performTraversals()函数展开的,该函数做的执 过程可简单概况
为
ViewRoot和DecorView
下图是View树结构,Activity中使 的findViewById() 法,就是在控件树中以树的深度优先遍历来查找对应的元素.每棵树的顶部都有 个ViewParent对象,是整棵树的控制核 ,所有的交互事件都由它统 调度和发配,从 可以对整个视图进 整体的控制
MeasureSpec
MeasureSpec
是 个32位int值,很 程度上决定 个View的尺 规格。受 View的影响。系统会将View的LayoutParams根据 View施加的规则转换成对应的MeasureSpec,再根据MeasureSpec来测 出View的宽/ 。
2位代表SpecMode |
低30位代表SpecSize |
UNSPECIGFIED: View 对View有限制,要多 给多 EXACTLY: View已经测出View的需要的精确 ,View最终的 就是SpecSize的值。对应
match_parent和具体数值AT_MOST: View指定 个可 的 SpecSize,View 能 于这个值,对应于wrap_content
MeasureSpec(http://www.alliedjeep.com/119103.htm)下面是MeasureSpec 的工作原理
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 makeSafeMeasureSpec(int size, int mode) {
if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
return 0; }
return makeMeasureSpec(size, mode); }
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);
int size = getSize(measureSpec);
if (mode == UNSPECIFIED) {
// No need to adjust size for UNSPECIFIED mode.
return makeMeasureSpec(size, UNSPECIFIED); }
size += 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); }
//... }
MeasureSpec(http://www.alliedjeep.com/119103.htm)普通View
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 sizeis 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 =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; }
MeasureSpec(http://www.alliedjeep.com/119103.htm)下 代码描述 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中的宽 的参数来分,LayoutParams.MATCH_PARENT:其模式为精确模式, 就是窗 的 LayoutParams.WRAP_CONTENT:其模式为最 模式, 定,但是 能超过窗 的 固定 ( 如100dp):其模式为精确模式, 为<span style="font-siz