安卓自定義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