核心三階段
Compose 將 UI 的生成與更新拆解為三個依序執行的獨立階段,以實現最大化的工作重用與性能優化。
- 組合 (Composition): 此階段的核心任務是執行 @Composable 函式,並產出一個描述 UI 結構的 UiNode 樹。它定義了「顯示什麼」,但尚未涉及尺寸或位置。當 State 變更時,Compose 會智能地僅「重組」受影響的節點,生成更新後的 UI 描述。
- 版面配置 (Layout): 此階段負責確定每個 UI 元素在螢幕上的尺寸與位置。它包含兩個子步驟:
- 測量 (Measure): 父元件向子元件傳遞約束 (Constraints),子元件回報其所需尺寸 (Size)。這是一個遞迴的、由上至下的過程。
- 放置 (Place): 父元件根據其佈局邏輯,將測量完成的子元件放置在特定的 (x, y) 座標。
- 繪製 (Drawing): 這是管線的最後一步。系統會遍歷佈局完成的 UI 樹,將其轉換為實際的 GPU 繪圖指令,最終在螢幕上渲染成像素。
由於階段分離,Compose 得以實現高效優化。例如,若元件僅位置變更,則可跳過組合階段,僅重跑佈局與繪製,從而節省運算資源。
Intrinsic Measurements
標準的單趟 (single-pass) 測量模型極為高效,但無法處理一種特定情況:當父元件的尺寸依賴於子元件的內容尺寸時。這便產生了一個佈局悖論——父元件無法在不了解子元件尺寸的情況下,為其提供準確的約束。
為此,Compose 引入了 Intrinsic Measurements。這是在正式測量前的一個「預先查詢」步驟。
- Query Pass: 父元件查詢子元件的「固有」尺寸 (min/maxIntrinsicWidth/Height),即其內容所需的理想大小。
- Final Pass: 父元件根據查詢結果決定自身尺寸後,再執行標準的測量與放置流程。
wrapContentSize 等修飾符的背後,正是利用此機制。然而,這種兩趟式 (two-pass) 測量必然比單趟測量成本更高,因此框架僅在必要時才啟用它。
你這麼一說,還真是,一語驚醒夢中人。
搞了半天,Compose 裡這套聽起來很潮的 Intrinsic Measurements,骨子裡不就是為了解決我們當年那個老問題嗎?
想當年,我們為了讓自定義 View 能完美處理 wrap_content,得在 onMeasure 裡跟 MeasureSpec 大戰三百回合。用位元運算去解析 AT_MOST、EXACTLY,寫一堆 if-else,小心翼翼地呼叫 setMeasuredDimension()
結果現在 Compose 倒好,它直接把這套流程抽象化了。它跟你說:「嘿,老兄,別寫那麼多指令了。我現在就問你一個問題,『你理想的寬度是多少?』,你給我個數字 (Int) 就行。」
問題還是那個問題:父容器需要先知道子元件的大小,才能決定自己。 解法也還是那個解法:需要兩趟測量。
差別在於,以前我們是那個親自下場、滿頭大汗的施工隊長;現在我們成了只提供一個數據的顧問。
只能感嘆,時代真的變了。以前我們是自己動手、指令分明,現在是跟框架「聊天」、回應提問。問題的本質沒變,但解法,確實優雅多了。
這套機制靠的就是的介面 (Interface)。
Modifier.layout 裡的那個 measurable 物件,它其實同時實現了兩個介面,等於能扮演兩種角色:
- Measurable : 它的 measure() 方法是最終命令。父佈局用它來下達「你必須是這個尺寸」的最終指示,走的是正規、單向的測量流程。
- IntrinsicMeasurable : 它的 min/maxIntrinsic… 等方法則用來回應「諮詢」。讓父佈局能預先探聽「你最希望自己多大?」的尺寸偏好。
To a composable, you can ask for itsIntrinsicSize.MinorIntrinsicSize.Max:Modifier.width(IntrinsicSize.Min)– What’s the minimum width you need to display your content properly?Modifier.width(IntrinsicSize.Max)– What’s the maximum width you need to display your content properly?Modifier.height(IntrinsicSize.Min)– What’s the minimum height you need to display your content properly?Modifier.height(IntrinsicSize.Max)– What’s the maximum height you need to display your content properly?