Android 自定義 ViewGroup 生命週期完整執行順序與 onMeasure/onLayout 實戰技巧
三種引用方式的生命週期差異
| 引用方式 | 執行順序 | 關鍵差異 |
| XML 佈局引用 | 構造器 → onFinishInflate() → onMeasure (多次) → onSizeChanged → onLayout → onMeasure/onLayout 循環 | 會執行 onFinishInflate (僅一次),適用 findViewById 初始化子 View |
| setContentView(new CustomView) | 構造器 → onMeasure → onSizeChanged → onLayout → ... | 無 onFinishInflate |
| new CustomView() 不加入父佈局 | 僅構造器 | 不觸發測量佈局流程 |

核心方法職責
- onFinishInflate():XML 解析完成、子 View 已建立,適合
findViewById、綁定事件
- onMeasure(int widthSpec, int heightSpec):測量自身與子 View 尺寸,**必須呼叫
setMeasuredDimension(w, h)** 確定自已尺寸
- onSizeChanged(w, h, oldw, oldh):尺寸發生變化時回調,適合依賴尺寸的初始化
- onLayout(changed, l, t, r, b):決定子 View 位置,呼叫
child.layout(l, t, r, b)
- draw/drawChild:繪製背景與子 View,通常不需重寫
onMeasure 實戰模板
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 1. 解析父容器施加的測量規格
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 2. 設置自身測量尺寸 (必須)
setMeasuredDimension(widthSize, heightSize);
// 3. 測量子 View
View leftMenu = getChildAt(1);
MarginLayoutParams lp = (MarginLayoutParams) leftMenu.getLayoutParams();
// 子 View 測量規格構建
int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec,
mMinDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width);
int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec,
lp.topMargin + lp.bottomMargin, lp.height);
leftMenu.measure(drawerWidthSpec, drawerHeightSpec);
View content = getChildAt(0);
lp = (MarginLayoutParams) content.getLayoutParams();
int contentWidthSpec = MeasureSpec.makeMeasureSpec(
widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);
int contentHeightSpec = MeasureSpec.makeMeasureSpec(
heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);
content.measure(contentWidthSpec, contentHeightSpec);
}
onLayout 實戰模板
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View content = getChildAt(0);
content.layout(l, t, r, b); // 佔滿全螢幕
View menu = getChildAt(1);
int menuWidth = menu.getMeasuredWidth();
menu.layout(-menuWidth, t, 0, b); // 初始隱藏在左側
}
getMeasuredWidth() vs getWidth()
| 方法 | 取得時機 | 數值穩定性 |
| getMeasuredWidth() | onMeasure 結束後 (setMeasuredDimension 後) | 固定不變 |
| getWidth() | onLayout 結束後 | 可能因佈局調整而變化 |
若 onLayout 未修改子 View 尺寸,兩者相等。
參考資源
Android ViewGroup 生命週期深度解析