Jetpack-Compose 學(xué)習(xí)筆記(五)—— State 狀態(tài)是個(gè)啥?又是新概念?

系列第五篇,進(jìn)入 Compose 中有關(guān) State 狀態(tài)的學(xué)習(xí)。

前面幾篇筆記講了那么多內(nèi)容,都是基于靜態(tài)界面的展示來(lái)說(shuō)的,即給我一個(gè)不變的數(shù)據(jù),然后將它展示出來(lái)。如何在 Compose 中構(gòu)建一個(gè)隨數(shù)據(jù)而變化的動(dòng)態(tài)界面呢?相信看完這篇就知道了。

1、基本知識(shí)

眾所周知,Compose 徹底舍棄了 xml 文件,我們需要像 Flutter 一樣完全用代碼去進(jìn)行界面的編碼,這樣做很容易會(huì)導(dǎo)致一個(gè)問(wèn)題:界面和數(shù)據(jù)處理邏輯耦合,從而導(dǎo)致 Activity 中代碼臃腫且維護(hù)性下降。

雖然提出了許多架構(gòu)思想,如 MVC、MVP、MVVM 等,一定程度上解耦了界面與數(shù)據(jù)處理邏輯,但是架構(gòu)本身就具有一定的復(fù)雜性,且對(duì)于后續(xù)維護(hù)成本也相對(duì)較高,所以 Compose 一開(kāi)始就將界面與數(shù)據(jù)分開(kāi)來(lái),分別稱(chēng)之為 組合State 狀態(tài)。

State 狀態(tài):官方文檔上說(shuō) State 狀態(tài)是指可以隨時(shí)間變化的任何值。例如,它可能是存儲(chǔ)在 Room 數(shù)據(jù)庫(kù)中的值、類(lèi)的變量,加速度計(jì)的當(dāng)前讀數(shù)等。
怎么理解這個(gè)概念呢?我覺(jué)得可以簡(jiǎn)單理解為:我們要展示給用戶看的數(shù)據(jù)。例如,一個(gè)商品的展示頁(yè)面,其實(shí)就是根據(jù)數(shù)據(jù)的不同來(lái)展示不同的狀態(tài),數(shù)據(jù)正常、數(shù)據(jù)錯(cuò)誤、空數(shù)據(jù)等不同的數(shù)據(jù)就是代表了不同的 State 狀態(tài)。

組合:按照文檔上的意思我覺(jué)得可以理解為展示給用戶的界面,是由多個(gè)組合項(xiàng)(Composable組件)組成。

Event事件:指的是從應(yīng)用外部生成的輸入,用于通知程序的某部分發(fā)生了變化。如用戶的點(diǎn)擊,滑動(dòng)等操作。所以在 Compose 中,Event 事件一般就是引起 State 狀態(tài)改變的原因。

2、狀態(tài)的表示

其實(shí)可以換一種說(shuō)法:Compose 中數(shù)據(jù)的存儲(chǔ)和更新如何處理?目前來(lái)看的話,可以用 LiveData、StateFlow、Flow、Observable 等表示??梢钥闯觯@些都是一種可觀察數(shù)據(jù)變化的容器,被它們修飾的對(duì)象,我們都可以觀察到該對(duì)象的變化,從而更新界面。沒(méi)錯(cuò),都是使用的觀察者模式。

在 Compose 的文檔中,ViewModel 被推薦為 State狀態(tài)的管理對(duì)象,從而實(shí)現(xiàn)將數(shù)據(jù)與界面展示的 Activity 分離解耦的目的。

2.1 ViewModel

ViewModel 也是 Jetpack 工具庫(kù)的成員之一,主要用來(lái)存儲(chǔ) UI 展示所需要的數(shù)據(jù),谷歌推薦的做法是將 Activity 中的數(shù)據(jù)都放到 ViewModel 里,而且在 Activity、Fragment 重建時(shí) ViewModel 中的數(shù)據(jù)是不受影響的。還可以通過(guò) ViewModel 來(lái)進(jìn)行 Activity 與 Fragment 之間,或者 Fragment 與 Fragment 之間的通信。

ViewModel 經(jīng)常與 LiveData 一起使用,但在 Compose 中,推薦使用 MutableState 來(lái)具體存儲(chǔ)數(shù)據(jù)的值。

2.2 MutableState<T>

MutableState<T> 是 Compose 中內(nèi)置的專(zhuān)門(mén)用于存儲(chǔ) State 狀態(tài)的容器,與 LiveData 一樣,它可以觀察到存儲(chǔ)的值的變化。如果項(xiàng)目不是純 Compose 代碼,建議還是用 LiveData,因?yàn)?LiveData 是通用的,而 MutableState<T> 是與 Compose 集成了,所以在 Compose 中使用 MutableState 比 LiveData 更簡(jiǎn)單。

從這里也可看出,Compose 是推薦將 State 狀態(tài)設(shè)置為可觀察的,這樣當(dāng)狀態(tài)發(fā)生更改時(shí),Compose 可以自動(dòng)重組更新界面。

實(shí)際上 MutableState<T> 是個(gè)接口:

// code 1
interface MutableState<T>: State<T> {
    override var value: T
}

對(duì) value 進(jìn)行的任何更改都會(huì)自動(dòng)重組用于讀取此狀態(tài)的所有 Composable 函數(shù),也就是說(shuō),value 值改變了之后,所有引用了 value 的 Composable 函數(shù)都會(huì)重新繪制更新。

3、一個(gè)簡(jiǎn)單例子

先來(lái)看看效果:


圖 1

其中有兩個(gè)控件,一個(gè)是 Text,用于顯示輸入的內(nèi)容;另一個(gè)是 TextField,相當(dāng)于 View 體系中的 EditText。可以看出,Text 顯示的內(nèi)容可以隨著下面的 TextField 中輸入的內(nèi)容實(shí)時(shí)更新。

如果是在 View 體系中,一般實(shí)現(xiàn)的方法是在 EditText 添加一個(gè) TextWatcher 類(lèi)用于監(jiān)聽(tīng)輸入事件,然后在 onTextChanged 方法中對(duì) TextView 設(shè)置輸入的內(nèi)容即可。

再來(lái)看一下 Compose 是如何實(shí)現(xiàn)這一小功能的 。根據(jù)官方推薦,得先有一個(gè) ViewModel 進(jìn)行狀態(tài)數(shù)據(jù)的管理:

// code 2
class ZhuViewModel: ViewModel() {
    // 狀態(tài)數(shù)據(jù)初始化,初始化為空字符串
    var inputStr = mutableStateOf("")
    // 狀態(tài)更新方法,將新輸入的內(nèi)容賦值給 MutableState<T> 對(duì)象的 value 值
    fun onInputChange(inputContent: String) {
        inputStr.value = inputContent
    }
}

可以看出,ViewModel 中需要對(duì)狀態(tài)進(jìn)行初始化,并且提供相應(yīng)的更新方法。同時(shí) ViewModel 中不會(huì)出現(xiàn)任何與界面相關(guān)的對(duì)象,例如 Activity、Fragment、Context 等,為的就是解耦。

界面代碼就是 Composable 函數(shù)根據(jù) ViewModel 管理的 State 狀態(tài)進(jìn)行展示:

// code 3
class ZhuStateActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val zhuViewModel by viewModels<ZhuViewModel>()
        setContent {InputShow(zhuViewModel)}
    }
}

@Composable
fun InputShow(viewModel: ZhuViewModel) {
    Column(Modifier.padding(20.dp)) {
        Text(text = viewModel.inputStr.value)
        TextField(
            value = viewModel.inputStr.value,
            onValueChange = { viewModel.onInputChange(it) }
        )
    }
}

TextField 組件相當(dāng)于 EditText,onValueChange 可獲取到用戶的輸入內(nèi)容,在這里調(diào)用 ViewModel 中更新?tīng)顟B(tài)的方法。這樣,所有引用了 ViewModel 中 MutableState 類(lèi)型對(duì)象 inputStr 的組合項(xiàng)(Composable 函數(shù)),都會(huì)自動(dòng)重繪更新,Text 組件就可以實(shí)時(shí)更新輸入的內(nèi)容了。

4. remember 關(guān)鍵字

其實(shí)在 code 3 中的小功能使用 ViewModel 來(lái)管理 State 狀態(tài)有點(diǎn)小題大做了,可以用 remember 關(guān)鍵字來(lái)實(shí)現(xiàn)。這個(gè)關(guān)鍵字的作用如它的意思一樣,“記住” 它所修飾的對(duì)象的值。下面的代碼就是沒(méi)有使用 ViewModel 的實(shí)現(xiàn)方法:

// code 4
class ZhuStateActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {InputShow()}
    }
}

@SuppressLint("UnrememberedMutableState")
@Composable
fun InputShow() {
    val inputStr = mutableStateOf("Hello")
    Column(Modifier.padding(20.dp)) {
        Text(text = inputStr.value)
        TextField(
            value = inputStr.value,
            onValueChange = {
                inputStr.value = it
            }
        )
    }
}

這里沒(méi)有使用 remember 會(huì)有紅線提醒,我先使用 SuppressLint 去掉了報(bào)錯(cuò),為的只是舉個(gè)栗子,并且設(shè)置了默認(rèn)展示 “Hello” 文案。運(yùn)行一下,你會(huì)發(fā)現(xiàn),不管輸入什么,都只是展示 “Hello”,好像啥也沒(méi)有發(fā)生。。。

這是為啥?加一些 log 看看:

// code 5
@SuppressLint("UnrememberedMutableState")
@Composable
fun InputShow() {
    val inputStr = mutableStateOf("Hello")
    Log.d(TAG, "InputShow: Column inputStr = ${inputStr.value}")
    Column(Modifier.padding(20.dp)) {
        Text(text = inputStr.value)
        TextField(
            value = inputStr.value,
            onValueChange = {
                inputStr.value = it
                Log.d(TAG, "InputShow: onValueChange inputStr = $it")
            }
        )
    }
}

連續(xù)輸入字母 w、o、r、l、d,打出來(lái)的 log 是這樣的:

圖 2

可見(jiàn)在每次輸入之后,都會(huì)觸發(fā) Composable 函數(shù)重新繪制,每次都會(huì)重新初始化 inputStr 這個(gè)狀態(tài),而初始值都是一樣的,所以看起來(lái)就好像輸入不起作用。Composable 函數(shù)的重新繪制過(guò)程也被稱(chēng)之為 重組。

重組:使用新的輸入Event事件重新調(diào)用可組合項(xiàng)以更新 Compose 樹(shù)的過(guò)程。這一過(guò)程會(huì)再次運(yùn)行相同的 Composable 組件進(jìn)行更新。

順帶說(shuō)一下,Compose 首次運(yùn)行渲染 Composable 組件時(shí),會(huì)為所有被調(diào)用的 Composable 組件構(gòu)建一個(gè)樹(shù),然后在重組期間會(huì)使用新的 Composable 組件去更新樹(shù)。

再回到這個(gè)例子,使用 remember 關(guān)鍵字就可以避免每次重組時(shí)都初始化為初始值。使用后的代碼為:

// code 6
@Composable
fun InputShow() {
    val inputStr = remember{ mutableStateOf("Hello") }
    Column(Modifier.padding(20.dp)) {
        Text(text = inputStr.value)
        TextField(
            value = inputStr.value,
            onValueChange = {
                inputStr.value = it
            }
        )
    }
}

這樣就可以正確實(shí)現(xiàn)功能了。其實(shí) remember 關(guān)鍵字的使用是由兩部分組成:

  1. key arguments:表示這次調(diào)用使用的 “鍵”(key),用圓括號(hào)包裹;
  2. calculation :一個(gè) Lambda 表達(dá)式,計(jì)算得出需要存儲(chǔ)的 “值”(value)。

所以,remember 的用法如下所示:

// code 7
remember(key) { calculation: () -> T }

remember 關(guān)鍵字可以為 Composable 組件項(xiàng)提供一個(gè)數(shù)據(jù)存儲(chǔ)空間,系統(tǒng)會(huì)將由 calculation Lambda 表達(dá)式計(jì)算得出的值存儲(chǔ)到組合樹(shù)中,只有當(dāng) remember 的 “鍵” 發(fā)生變化時(shí),才會(huì)重新執(zhí)行 calculation 計(jì)算得出 value 并存儲(chǔ)起來(lái);否則還是原來(lái)的值。

當(dāng)然 code 6 中并沒(méi)有設(shè)置 remember 的 key,這種情況下,remember 會(huì)默認(rèn)該 key 沒(méi)有發(fā)生變化,不會(huì)重新初始化,而是用之前的值。

需要注意的點(diǎn): remember 雖然會(huì)將數(shù)據(jù)或?qū)ο蟠鎯?chǔ)在組合項(xiàng)中,但當(dāng)調(diào)用 remember 的可組合項(xiàng)從組合樹(shù)中移除后,它會(huì)忘記該數(shù)據(jù)或?qū)ο蟆K?,不要在有添加或移?Composable 組件的情況下,使用 remember 將重要內(nèi)容存儲(chǔ)在 Composable 組件中,因?yàn)樘砑雍鸵瞥紩?huì)使得數(shù)據(jù)丟失。

5. 狀態(tài)提升

狀態(tài)提升的概念是對(duì)于 Composable 組件來(lái)說(shuō)的,根據(jù) Composable 組件中是否含有 State 狀態(tài)可分為 有狀態(tài)可組合項(xiàng)無(wú)狀態(tài)可組合項(xiàng)。 如 code 6 中的 InputShow 組合項(xiàng)就是一個(gè)有狀態(tài)可組合項(xiàng)。

5.1 有狀態(tài)與無(wú)狀態(tài)

Flutter 中的 Widget 也是分為 StatefulWidget 和 StatelessWidget,想不到 Compose 也借用了這個(gè)設(shè)計(jì)思想。

有狀態(tài)可組合項(xiàng)是一種具有可隨時(shí)間變化狀態(tài)的 Composable 組件。再說(shuō)具體一點(diǎn),就是 Composable 組件里有類(lèi)似于 remember 存儲(chǔ)的狀態(tài),而且該組件會(huì)在內(nèi)部保持和改變自己的狀態(tài)。調(diào)用方不需要控制狀態(tài)。缺點(diǎn)是,具有內(nèi)部狀態(tài)的可組合項(xiàng)復(fù)用性往往不高,也更難以測(cè)試。

無(wú)狀態(tài)可組合項(xiàng)就是指無(wú)法直接更改任何狀態(tài)的 Composable 組件。因?yàn)椴话魏螤顟B(tài)數(shù)據(jù),所以它更容易測(cè)試,復(fù)用性也更高。

如果需要將有狀態(tài)組合項(xiàng)轉(zhuǎn)變?yōu)闊o(wú)狀態(tài)組合項(xiàng),則需要 狀態(tài)提升。

5.2 狀態(tài)提升怎么做?

Compose 中的狀態(tài)提升是一種將狀態(tài)移至可組合項(xiàng)的調(diào)用方以使可組合項(xiàng)無(wú)狀態(tài)的模式。常規(guī)的狀態(tài)提升模式是將狀態(tài)變量替換為兩個(gè)參數(shù):

  1. value: T:要顯示的當(dāng)前值;
  2. onValueChange: (T) -> Unit:請(qǐng)求更改值的事件,其中的 T 是新值

這種方式提升的狀態(tài)具有一些重要的屬性:

  1. 單一可信來(lái)源: 狀態(tài)提升并不是將狀態(tài)復(fù)制,而是將狀態(tài)移動(dòng)到上層的可組合項(xiàng)中,這樣可確保只有一個(gè)可信來(lái)源,減少數(shù)據(jù)不一致所導(dǎo)致的 bug;
  2. 封裝: 只有有狀態(tài)可組合項(xiàng)可以修改其狀態(tài),可以理解為是內(nèi)部“自治”的;
  3. 可共享: 提升后的狀態(tài)可以與多個(gè)可組合項(xiàng)共享;
  4. 可攔截: 無(wú)狀態(tài)可組合項(xiàng)的調(diào)用方可以在更改狀態(tài)之前決定忽略或者修改事件;
  5. 解耦: 無(wú)狀態(tài)可組合項(xiàng)的狀態(tài)可以存儲(chǔ)在任何位置,如 ViewModel 中。

具體怎么做可以看下面的一個(gè)小栗子。

5.3 狀態(tài)提升小栗子

根據(jù)上述所說(shuō),很容易就可以得知 code 6 的 InputShow Composable 組件是一個(gè)有狀態(tài)的可組合項(xiàng),它包含一個(gè)狀態(tài)變量 inputStr,所以,我們要將這個(gè) MutableState 用兩個(gè)參數(shù)進(jìn)行替換,一個(gè)是要顯示的當(dāng)前值;另一個(gè)是 Lambda 表達(dá)式,用于請(qǐng)求更改值的事件,就可以將其改寫(xiě)為一個(gè)無(wú)狀態(tài)可組合項(xiàng)。如下 code 8 所示:

// code 8 無(wú)狀態(tài)可組合項(xiàng) InputShow
@Composable
fun InputShow(inputText: String, onInputChange: (String)-> Unit) {
    Column(Modifier.padding(20.dp)) {
        Text(text = inputText)
        TextField(
            value = inputText,
            onValueChange = onInputChange
        )
    }
}

那狀態(tài)提升到哪里去了呢?通常會(huì)提升到它的父組件中,那么父組件就是一個(gè)有狀態(tài)的可組合項(xiàng)了,這個(gè)例子中 InputShow 的父組件這里定義為 InputShowContainer:

// code 9
@Composable
fun InputShowContainer() {
    val (inputStr, setInput) = remember{ mutableStateOf("") }
    InputShow(inputStr, setInput)
}

嗯?MutableState 的聲明與之前的不太一樣了,多出來(lái)的這個(gè) setInput 也是一個(gè) Lambda 表達(dá)式,用于更新值。其實(shí),聲明 MutableState 對(duì)象的方法總共有三種:

  1. val mutableState = remember{ mutableStateOf(default) }
  2. val value by remember{ mutableStateOf(default) }
  3. val (value, setValue) = remember{ mutableStateOf(default) }

所以這里用的是第三種聲明方法。這樣,InputShow 組合項(xiàng)就經(jīng)過(guò)狀態(tài)提升變?yōu)榱藷o(wú)狀態(tài)的可組合項(xiàng)了。官方在這里還特意說(shuō)明,在 Composable 組件中創(chuàng)建 State<T>(或其他有狀態(tài)對(duì)象)時(shí),務(wù)必對(duì)其執(zhí)行 remember 操作,否則它會(huì)在每次重組時(shí)重新初始化。

6. 狀態(tài)存儲(chǔ)的其他方式

由前述所說(shuō),remember 關(guān)鍵字可存儲(chǔ)組合項(xiàng)中的狀態(tài),但是一旦組合項(xiàng)被移動(dòng),這些狀態(tài)就丟失了,那如果涉及到橫豎屏切換等 Activity 重建的應(yīng)用場(chǎng)景,該怎么辦呢?雖然保存在 ViewModel 中可以解決問(wèn)題,但總有點(diǎn)小題大做了。下面是狀態(tài)存儲(chǔ)的一些其他的方式。

6.1 rememberSaveable

這個(gè)與 remember 類(lèi)似,主要用于 Activity 或進(jìn)程重建時(shí),恢復(fù)界面狀態(tài)。還是上面 code 6 的栗子,可以試試橫豎屏切換或其他配置項(xiàng)更改,會(huì)發(fā)現(xiàn)使用 remember 關(guān)鍵字時(shí),切換后就回到初始空白值了。改為 rememberSaveable 后切換后輸入的內(nèi)容可以保存下來(lái)而不會(huì)被重置。

這么看的話,rememberSaveable 有點(diǎn)像是 override fun onSaveInstanceState(outState: Bundle)
方法了,確實(shí)是這樣的,任何可以存儲(chǔ)在 Bundle 對(duì)象中的數(shù)據(jù)都可以通過(guò) rememberSaveable 進(jìn)行保存。無(wú)法用 Bundle 進(jìn)行保存的數(shù)據(jù),可以用下面的方式進(jìn)行存儲(chǔ)。

6.2 Parcelize

最簡(jiǎn)單的解決方法就是在對(duì)象上添加 @Parcelize 注解,對(duì)象就可以轉(zhuǎn)化為可打包狀態(tài)且可以捆綁。還記得 Java 中的 Serializable 接口嗎?是一樣的作用,都是將實(shí)例對(duì)象編碼成字節(jié)流進(jìn)行存儲(chǔ)。

在日常 Android 開(kāi)發(fā)中如果不涉及到本地化存儲(chǔ)或者網(wǎng)絡(luò)傳輸?shù)那闆r,推薦使用 Parcelable,因?yàn)橄啾扔?Serializable 它不會(huì)產(chǎn)生大量臨時(shí)對(duì)象,沒(méi)有使用反射,效率更高。但很多時(shí)候不想寫(xiě) Parcelable 接口的模板代碼,那么就可以使用這個(gè)注解!下面是樣例及使用步驟:

// code 10
// app/build.gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-parcelize'    // 第一步:添加此插件
}

@Parcelize    // 第二步:添加注解及 Parcelable 接口
data class City(val name: String, val country: String) : Parcelable

// 這樣就可以將其保存到狀態(tài)中
val cityBean = rememberSaveable{ mutableStateOf(City("0112","西京"))}

終于,Parcelable 和 Serializable 接口一樣好用了!

6.3 MapSaver

Compose 還考慮到有些情況下 Parcelize 不適用的場(chǎng)景,那么還可以使用 MapSaver 來(lái)定義自己的存儲(chǔ)和恢復(fù)規(guī)則,規(guī)定如何把對(duì)象轉(zhuǎn)為可保存到 Bundle 中的值。

// code 11
data class Book(val name: String, val author: String)

val BookSaver = run {
    val nameKey = "Name"
    val authorKey = "Author"
    mapSaver(
        save = { mapOf(nameKey to it.name, authorKey to it.author) },
        restore = { Book(it[nameKey] as String, it[authorKey] as String) }
    )
}

val chosenBook = rememberSaveable( stateSaver = BookSaver ) {
    mutableStateOf(Book("三體","劉慈欣"))
}

核心在 BookSaver 這個(gè) Saver 對(duì)象,通過(guò) save 這個(gè) lambda 可以將 Book 對(duì)象轉(zhuǎn)化為一個(gè) Map 進(jìn)行存儲(chǔ);要使用的時(shí)候就通過(guò) restore 這個(gè) lambda 將 Map 又恢復(fù)為一個(gè) Book 對(duì)象。

6.4 ListSaver

MapSaver 需要自己去定義 Key 值,但使用 ListSaver 就可以不用自己定義 Key,本質(zhì)上是把對(duì)象放在一個(gè) List 中存儲(chǔ),所以它是使用索引作為 Key。

// code 12
val BookListSaver = listSaver<Book, Any>(
    save = { listOf(it.name, it.author) },
    restore = { Book(it[0] as String, it[1] as String) }
)

使用起來(lái)與 MapSaver 一樣,只不過(guò)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)不一樣罷了。實(shí)際上,MapSaver 底層也是用 ListSaver 實(shí)現(xiàn)的。

總結(jié)

最后來(lái)個(gè)總結(jié)吧。

  • Compose 為了實(shí)現(xiàn)解耦將界面和數(shù)據(jù)分離開(kāi)來(lái),分別稱(chēng)之為 組合 與 State 狀態(tài)。為了達(dá)到狀態(tài)改變自動(dòng)重組界面的目的,引入了 MutableState<T> 來(lái)存儲(chǔ) State 狀態(tài)的容器。
  • MutableState<T> 的 value 一旦改變,所有引用它的 Composable 組件都會(huì)重組,從而保證了數(shù)據(jù)與顯示的一致性。此外,為了保證每次重組時(shí) State 狀態(tài)不會(huì)被初始化為初值,Compose 引入 remember 關(guān)鍵字來(lái)將數(shù)據(jù)存儲(chǔ)在相應(yīng)的 Composable 組件中。
  • remember 關(guān)鍵字是根據(jù)傳入的鍵是否改變來(lái)返回相應(yīng)的值。鍵改變了則返回初值;鍵未變則返回上次存儲(chǔ)的值。不設(shè)置鍵,則默認(rèn)鍵始終不變,即始終取上次的值。
  • 為了解決 remember 關(guān)鍵字不能在 Activity 重建等場(chǎng)景下保存數(shù)據(jù)而引入了 rememberSaveable、MapSaver、ListSaver 等狀態(tài)保存及恢復(fù)的方法。
  • Compose 把 Composable 組件分為有狀態(tài)與無(wú)狀態(tài)兩類(lèi),內(nèi)部含有 State 狀態(tài)的就為有狀態(tài)可組合項(xiàng);反之則為無(wú)狀態(tài)組合項(xiàng)。無(wú)狀態(tài)組合項(xiàng)復(fù)用性更高,而有狀態(tài)組合項(xiàng)可以自己管理State狀態(tài)。通過(guò)狀態(tài)提升可以將有狀態(tài)組合項(xiàng)轉(zhuǎn)化為無(wú)狀態(tài)組合項(xiàng)。
  • Compose 推薦使用 ViewModel 來(lái)管理狀態(tài),包括狀態(tài)的更新以及存儲(chǔ)等。

參考文獻(xiàn)

  1. 官方文檔——在Jetpack Compose 中使用狀態(tài) https://developer.android.google.cn/codelabs/jetpack-compose-state?
  2. Compose 狀態(tài)與組合 新小夢(mèng) https://juejin.cn/post/6937560914254102565
  3. 【背上Jetpack之ViewModel】即使您不使用MVVM也要了解ViewModel ——ViewModel 的職能邊界 Flywith24 https://juejin.cn/post/6844904100493017095
  4. Jetpack Compose學(xué)習(xí)之mutableStateOf與remember是什么 柚子君下 https://blog.csdn.net/weixin_43662090/article/details/116120540
  5. 官方文檔——狀態(tài)和 Jetpack Compose https://developer.android.google.cn/jetpack/compose/state

更多內(nèi)容,歡迎關(guān)注我的同名公眾號(hào)留言交流~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容