核心三階段
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.Min
orIntrinsicSize.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?