今天來介紹會觸發畫面更新的 State。先來個大總結,後頭再解釋:
可觀察且可變
例如:State<List<T>>
不可變
例如:listOf()
可變對象
例如: ArrayList<T>
、 mutableListOf()
、自定義物件 ( 這個很常遇到要注意!)
原因:在 Compose 中可變對象作為狀態會導致使用者在 app 中看到不正確或舊的 Data。
不可觀察且可變對象
例如: ArrayList<T>
、var
原因:不能引發重組
在寫 Compose 的時候,有沒有想過 Compose 是怎麼觸發畫面更新的呢? Compose 採用聲明式 — 也就是參數傳入讓 function 觸發重構畫面。因此不能像XML 那樣子 view.setXXX
這種命令式去改變畫面。
我剛開始學 Compose 時就遇到,點擊按鈕改變點擊次數 times
,然而 Text
顯示卻不會改變。
@Preview(showBackground = true)
@Composable
fun StateSample() {
var times = 0
Column {
Text(text = times.toString())
Button(onClick = { times = times++ }) {
Text("Click")
}
}
}
才發現原來要觸發的不只是改變值,這個值還要能去通知 Compose 你該重組更新畫面囉!
有這樣性質的變數,並須要有狀態 State
。要將變數改成下面這樣
@Preview(showBackground = true)
@Composable
fun StateSample() {
var timesState by remember { mutableStateOf(0) }
Column {
Text(text = "$timesState")
Button(onClick = { timesState++ }) {
Text("Click")
}
}
}
這裡有幾個關鍵字
by
委託,可以利用 by 將 getValue setValue 給搞定。remember
能防止 compose 重組後,連同裡面的變數也初始化了。mutableStaeOf()
State 可以觸發重組、mutable 則是可變的意思。rememberSaveable
不怕畫面旋轉雖然 remember
可以在重組間保留狀態,但只要設定有所變更,狀態就無法保留,例如畫面旋轉這種整個邊寬都大改的情況,畫面必須重新測量,這時remember
就無法記得值了。
必須使用 rememberSaveable
。rememberSaveable
會把值存在 Bundle
,就可以保有重繪前的值。
這裡要注意 Bundle
只能存kotlin支援的基本型態。那麼 Bundle 不能存的物件要怎麼辦呢?
有以下三種方法
@Parcelize
data class City(val name: String, val country: String) : Parcelable
@Composable
fun CityScreen() {
var selectedCity = rememberSaveable {
mutableStateOf(City("Madrid", "Spain"))
}
}
MapSaver 自行定義規則,用來將物件轉換為一組可讓系統儲存至 Bundle
的值。
data class City(val name: String, val country: String)
val CitySaver = run {
val nameKey = "Name"
val countryKey = "Country"
mapSaver(
save = { mapOf(nameKey to it.name, countryKey to it.country) },
restore = { City(it[nameKey] as String, it[countryKey] as String) }
)
}
@Composable
fun CityScreen() {
var selectedCity = rememberSaveable(stateSaver = CitySaver) {
mutableStateOf(City("Madrid", "Spain"))
}
}
存成像是 list 的樣子,他的 key 直接就是 index。
data class City(val name: String, val country: String)
val CitySaver = listSaver<City, Any>(
save = { listOf(it.name, it.country) },
restore = { City(it[0] as String, it[1] as String) }
)
@Composable
fun CityScreen() {
var selectedCity = rememberSaveable(stateSaver = CitySaver) {
mutableStateOf(City("Madrid", "Spain"))
}
}
請回到文章前言去看~
今日運動
硬舉
35kg 15 15 15 下
55kg 10 15 15 下
59.5kg 10 6 下
反手下拉(背)
20kg 15 15 下
22.5kg 8 7 下
17.5kg 9 15 下
瑜伽伸展 50 分鐘