iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

前言


今天來介紹會觸發畫面更新的 State。先來個大總結,後頭再解釋:

在 Compose 中建議使用以下方式儲存變數

  1. 可觀察且可變

    例如:State<List<T>>

  2. 不可變

    例如:listOf()

在 Compose 中不建議使用以下方式儲存變數

  1. 可變對象

    例如: ArrayList<T> 、 mutableListOf() 、自定義物件 ( 這個很常遇到要注意!)

    原因:在 Compose 中可變對象作為狀態會導致使用者在 app 中看到不正確或舊的 Data。

  2. 不可觀察且可變對象

    例如: ArrayList<T>var

    原因:不能引發重組

State 是什麼?可以用來幹嘛?


在寫 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 就無法記得值了。

必須使用 rememberSaveablerememberSaveable 會把值存在 Bundle ,就可以保有重繪前的值。

這裡要注意 Bundle 只能存kotlin支援的基本型態。那麼 Bundle 不能存的物件要怎麼辦呢?

Bundle 不能存的物件要怎麼保留?


有以下三種方法

1. 使用 @Parcelize 可打包的狀態

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

2. MapSaver 對應成 bundle 能存的值

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"))
    }
}

3. ListSaver 不想用 key-value

存成像是 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"))
    }
}

總結


請回到文章前言去看~

參考


State In Compose

今日運動
硬舉
35kg 15 15 15 下
55kg 10 15 15 下
59.5kg 10 6 下
反手下拉(背)
20kg 15 15 下
22.5kg 8 7 下
17.5kg 9 15 下
瑜伽伸展 50 分鐘


上一篇
Day 23 溫咖癲啦唯啊薩~讓按鈕浮起來吧!Floating Action Button
下一篇
Day 25 Advance State 和 Side Effect
系列文
今年一定減成功!Jetpack Compose 做出重訓紀錄APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言