Jetpack Compose 是一種響應式的 UI 框架,當我們更新 UI 的狀態時,Compose 會自動更新 UI。
為了達成這個目標,Jetpack Compose 使用 State 來記錄與感知 UI 狀態的變化。
State 是一種特別的變數,會隨著 APP 的運行而發生變化,進而改變 UI 的外觀。
程式碼範例
假設我們想要建立一個計數器 (Counter),每按一下,就把 count 這個變數加 1
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
StateLabTheme {
Column (
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxSize()
) {
Counter()
}
}
}
}
}
private var count = 0 // 這不是 state
@Composable
fun Counter() {
Button(
onClick = {
count++
Log.d("TAG", "count: $count")
}
) {
Text("Count: $count")
}
}
這個 app 有個問題,當我們按下按鈕後,可以在 Logcat 裏看到 count 變數的變化,但是畫面上的數字依舊是 0,不會改變。
這是因為 count 不是 State,所以 Compose UI 不會被通知到 count 這個變數已經改為,需要重畫 (Recompose) UI 畫面。
建立一個 state 變數
我們稍微修改一下程式碼,把 count 改為 state 變數
...
// 這個是 state 變數
private var count = mutableIntStateOf(0)
@Composable
fun Counter() {
Button(
onClick = {
count.intValue++
}
) {
Text("Count: ${count.intValue}")
}
}
...
現在我們再按下按鈕,按鈕上的計數就會隨之改變。
使用 delegate 來簡化程式
加入兩個 import 敘述,我們就可以用 by 語法來簡化程式
...
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
...
private var count by mutableIntStateOf(0)
@Composable
fun Counter() {
Button(
onClick = {
count++
}
) {
Text("Count: $count")
}
}
...
將 state 移至 composable 中
一般而言,我們不會使用全域變數來存放 state。
有一種比較簡單的方式,是透過 remember 系列的函式,把 state 放在 composable function 中。
這樣當 recompose 時,變數的值才不會因重置而丟失,畢竟它被記住 (remember) 了
...
//private var count by mutableIntStateOf(0)
@Composable
fun Counter() {
var count by remember {
mutableIntStateOf(0)
}
Button(
onClick = {
count++
}
) {
Text("Count: $count")
}
}
...
另一種方式是把 state 移到 ViewModel 中,這個方式其實是比較主流的實作方式,等有機會再討論。
Comments