在 Compose 中,使用者介面無法變更,即後無法更新
繪製。您可以控制的是 UI 狀態,每當
UI 變更,Compose 會重新建立
已變更。可組合函式可接受
並公開事件,例如 TextField
會接受值並公開
回呼 onValueChange
,要求回呼處理常式變更
值。
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
由於可組合函式接受狀態並公開事件,因此單向資料流 最適合搭配 Jetpack Compose 使用本指南著重介紹 Compose 的單向資料流模式、如何實作事件及 狀態容器,以及如何在 Compose 中使用 ViewModel。
單向資料流程
單向資料流 (UDF) 是狀態向下流動的設計模式 事件數量和事件都會增加只要跟隨單向資料流,您就能分離 這些可組合函式會在應用程式儲存的部分顯示狀態的 UI 狀態 以及變更狀態
使用單向資料流的應用程式 UI 更新迴圈如下所示:
- 事件:部分 UI 產生事件並向上傳遞,例如 傳遞至 ViewModel 處理的按鈕點選動作;或者是 例如指出使用者工作階段 已過期。
- 更新狀態:事件處理常式可能會變更狀態。
- 顯示狀態:狀態容器向下傳遞狀態,然後在 UI 中顯示。 基礎架構
![](https://developer.android.com/static/develop/ui/compose/images/state-unidirectional-flow.png?authuser=0000&hl=zh-tw)
使用 Jetpack Compose 時遵循這個模式可提供幾個優點:
- 可測試性:從使用者介面分離狀態,更容易 分別進行測試
- 狀態封裝:由於狀態只能在一個位置更新, 可組合函式狀態只會有一個可靠資料來源, 您可能會因為狀態不一致而產生錯誤
- UI 一致性:
使用可觀測的狀態容器,例如
StateFlow
或LiveData
。
Jetpack Compose 的單向資料流
基於狀態和事件的可組合函式運作。舉例來說,TextField
僅
當 value
參數更新且會顯示 onValueChange
時
回呼:要求將值變更為新事件的事件。撰寫
將 State
物件定義為值容器,並變更狀態值
才會觸發重組程序您可以將狀態保留在
remember { mutableStateOf(value) }
或
rememberSaveable { mutableStateOf(value)
,取決於您需要的時間長度
請記住 值
TextField
可組合值的類型為 String
,因此可以
從任何位置存取:透過硬式編碼值、從 ViewModel,或從
父項可組合函式。您不一定要將其保存在 State
物件中,但您需要
,在呼叫 onValueChange
時更新值。
定義可組合函式的參數
定義可組合函式的狀態參數時,應注意下列事項 幾個關鍵問題:
- 可組合項的可重複使用或靈活性如何?
- 狀態參數對這個可組合函式的效能有何影響?
為了鼓勵分離和重複使用,每個可組合項應盡可能減少 資訊例如,建構可組合元件時 新聞文章的標題,最好只傳入 而不是整篇新聞報導:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
有時候,使用個別參數也可以提升成效,例如
News
包含多個資訊,不只是 title
和 subtitle
,時
新的 News
執行個體會傳遞至 Header(news)
,可組合函式會將
重組。title
subtitle
請仔細考量您傳入的參數數量。讓函式具有 參數過多會降低函式的人體工學,因此在本例中 最好是將這些畫面組合成一個類別
Compose 中的事件
應用程式的所有輸入內容都應以事件表示:輕觸、文字變更、
甚至是計時器或其他最新動態當這些事件變更 UI 狀態時
ViewModel
應該是處理這些程式碼並更新 UI 狀態的那個。
UI 層一律不得變更事件處理常式以外的狀態,因為 可能會在應用程式中造成不一致和錯誤。
最好傳遞狀態和事件處理常式 lambda 的不可變值。這個 的好處如下:
- 提高可重複使用性。
- 請確保 UI 不會直接變更狀態值。
- 避免並行問題,因為您可以確保狀態不會 已從另一個執行緒變動。
- 通常,您可以降低程式碼的複雜度。
舉例來說,接受 String
和 lambda 做為參數的可組合項可以
非常容易重複使用假設熱門應用程式
則應用程式中一律顯示文字並提供返回按鈕。您可以定義
較通用的 MyAppTopAppBar
可組合函式,可接收文字和背面
按鈕控點做為參數:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
範例:ViewModel、狀態和事件
使用 ViewModel
和 mutableStateOf
也可以引入單向資料
流程。
- 系統會透過可觀測的狀態容器 (例如
StateFlow
或LiveData
) 公開 UI 狀態。 ViewModel
會處理來自 UI 或其他應用程式層的事件 並根據事件更新狀態容器。
舉例來說,如要實作登入畫面,請輕觸「登入」按鈕 ,應用程式應會顯示進度旋轉圖示和網路呼叫。如果 登入成功,然後您的應用程式會切換到其他畫面;在 應用程式顯示 Snackbar 錯誤。以下說明如何建立螢幕狀態的模型 以及事件:
畫面共有四種狀態:
- 已登出:使用者尚未登入。
- 進行中:應用程式目前嘗試讓使用者透過 以及執行網路呼叫
- 錯誤:登入時發生錯誤。
- 已登入:使用者已登入。
您可以模擬這些狀態,做為密封類別。ViewModel
會將狀態顯示為
State
、設定初始狀態,並視需要更新狀態。
ViewModel
也會公開 onSignIn()
方法,處理登入事件。
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
除了 mutableStateOf
API 之外,Compose 也提供
適用於 LiveData
、Flow
和 的擴充功能
Observable
註冊為事件監聽器並將值顯示為狀態。
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
瞭解詳情
如要進一步瞭解 Jetpack Compose 中的架構,請參閱下列資源:
範例
為您推薦
- 注意:系統會在 JavaScript 關閉時顯示連結文字
- 狀態和 Jetpack Compose
- 在 Compose 中儲存 UI 狀態
- 處理使用者輸入內容