JetPack Compose 基礎(chǔ)(4) state

狀態(tài)以及 Jetpack Compose 如何使用和操作狀態(tài)饰及。

在我們深入研究之前,定義狀態(tài)到底是什么很有用。 從本質(zhì)上講幻馁,應(yīng)用程序中的狀態(tài)是任何可以隨時(shí)間變化的值洗鸵。 這是一個(gè)非常廣泛的定義,包括從 Room 數(shù)據(jù)庫到類上的變量的所有內(nèi)容仗嗦。

目標(biāo):

什么是單向數(shù)據(jù)流
如何在 UI 中考慮狀態(tài)和事件
如何在 Compose 中使用 Architecture Component 的 ViewModel 和 LiveData 來管理狀態(tài)
Compose 如何使用狀態(tài)來繪制屏幕
何時(shí)將狀態(tài)移動(dòng)到調(diào)用者
如何在 Compose 中使用內(nèi)部狀態(tài)
如何使用 State<T> 將狀態(tài)與 Compose 集成

state_ui.png

使用單向數(shù)據(jù)流
為了幫助解決非結(jié)構(gòu)化狀態(tài)的這些問題膘滨,我們引入了包含 ViewModel 和 LiveData 的 Android 架構(gòu)組件。

ViewModel 允許您從 UI 中提取狀態(tài)并定義 UI 可以調(diào)用以更新該狀態(tài)的事件

官方的例子理解原理:

ViewModel 還公開了一個(gè)事件:onNameChanged稀拐。 此事件由 UI 調(diào)用以響應(yīng)用戶事件火邓,例如每當(dāng) EditText 的文本更改時(shí)此處會(huì)發(fā)生什么。

回到我們之前討論過的 UI 更新循環(huán)钩蚊,我們可以看到這個(gè) ViewModel 如何與事件和狀態(tài)結(jié)合在一起贡翘。

事件 – onNameChanged 在文本輸入更改時(shí)由 UI 調(diào)用
更新狀態(tài) - onNameChanged 進(jìn)行處理蹈矮,然后設(shè)置 _name 的狀態(tài)
顯示狀態(tài) - 調(diào)用名稱的觀察者砰逻,通知 UI 狀態(tài)變化
通過以這種方式構(gòu)建我們的代碼,我們可以認(rèn)為事件“向上”流向 ViewModel泛鸟。 然后蝠咆,為了響應(yīng)事件,ViewModel 將進(jìn)行一些處理并可能更新狀態(tài)北滥。 當(dāng)狀態(tài)更新時(shí)刚操,它會(huì)“向下”流向
viemodel_state_event_activity.png

這種模式稱為單向數(shù)據(jù)流。 單向數(shù)據(jù)流是一種狀態(tài)向下流動(dòng)而事件向上流動(dòng)的設(shè)計(jì)再芋。 通過以這種方式構(gòu)建我們的代碼菊霜,我們獲得了一些優(yōu)勢:

  • 可測試性——通過將狀態(tài)與顯示它的 UI 分離,可以更輕松地測試 ViewModel 和 Activity
  • 狀態(tài)封裝——因?yàn)闋顟B(tài)只能在一個(gè)地方(ViewModel)更新济赎,隨著 UI 的增長鉴逞,你不太可能引入部分狀態(tài)更新錯(cuò)誤
  • UI 一致性——所有狀態(tài)更新都通過使用可觀察狀態(tài)持有者立即反映在 UI 中

因此,雖然這種方法確實(shí)添加了更多代碼司训,但使用單向數(shù)據(jù)流處理復(fù)雜的狀態(tài)和事件往往更容易构捡、更可靠。

單向數(shù)據(jù)流是一種事件向上流動(dòng)而狀態(tài)向下流動(dòng)的設(shè)計(jì)壳猜。
例如勾徽,在 ViewModel 中,事件通過來自 UI 的方法調(diào)用傳遞统扳,而狀態(tài)使用 LiveData 向下流動(dòng)喘帚。
它不僅僅是描述 ViewModel 的術(shù)語——任何事件向上流動(dòng)和狀態(tài)下降的設(shè)計(jì)都是單向的。

如何使用 ViewModel 在 Compose 中使用單向數(shù)據(jù)流咒钟。

上面理論吹由,使用 ViewModel 和 LiveData 探索了 Android View 系統(tǒng)中的單向數(shù)據(jù)流。

StateCodeLab 項(xiàng)目 解讀

TodoScreen.kt – 這些可組合項(xiàng)直接與狀態(tài)交互盯腌,我們將在探索 compose 狀態(tài)時(shí)編輯此文件溉知。
TodoComponents.kt – 這些可組合定義了我們將用于構(gòu)建 TodoScreen 的可重用 UI 位。 您無需編輯這些可組合項(xiàng)即可完成此 Codelab。
這種文件劃分有點(diǎn)隨意,將 TodoScreen.kt 中的代碼集中在狀態(tài)上埃疫。 在實(shí)踐中研叫,這些組合項(xiàng)可能位于同一個(gè)文件中,或者分布在多個(gè)文件中甚淡,具體取決于您在項(xiàng)目中如何使用它們。

  1. TodoScreen 函數(shù)

這個(gè)可組合顯示一個(gè)可編輯的 TODO 列表捅厂,但它沒有任何自己的狀態(tài)贯卦。 請記住,狀態(tài)是任何可以更改的值——但 TodoScreen 的任何參數(shù)都不能修改焙贷。

items – 要顯示在屏幕上的不可變項(xiàng)目列表
onAddItem – 用戶請求添加項(xiàng)目時(shí)的事件
onRemoveItem – 用戶請求刪除項(xiàng)目時(shí)的事件

事實(shí)上撵割,這個(gè)可組合是無狀態(tài)的。 它只顯示傳入的項(xiàng)目列表辙芍,無法直接編輯列表啡彬。 相反,它傳遞了兩個(gè)可以請求更改的事件 onRemoveItem 和 onAddItem故硅。

這就提出了一個(gè)問題:如果它是無狀態(tài)的庶灿,它如何顯示可編輯列表? 它通過使用一種稱為狀態(tài)提升的技術(shù)來做到這一點(diǎn)吃衅。 狀態(tài)提升是向上移動(dòng)狀態(tài)以使組件無狀態(tài)的模式往踢。 無狀態(tài)組件更容易測試,往往有更少的錯(cuò)誤徘层,并提供更多的重用機(jī)會(huì)峻呕。

事件 – 當(dāng)用戶請求添加或刪除項(xiàng)目時(shí) TodoScreen 調(diào)用 onAddItem 或 onRemoveItem
更新狀態(tài)——TodoScreen 的調(diào)用者可以通過更新狀態(tài)來響應(yīng)這些事件
顯示狀態(tài) - 當(dāng)狀態(tài)更新時(shí),TodoScreen 將使用新項(xiàng)目再次調(diào)用惑灵,并且可以在屏幕上顯示它們

調(diào)用者負(fù)責(zé)確定在何處以及如何保持此狀態(tài)山上。 它可以存儲項(xiàng)目但有意義,例如在內(nèi)存中或從 Room 數(shù)據(jù)庫中讀取它們英支。 TodoScreen 與狀態(tài)的管理方式完全分離佩憾。

使用這個(gè) ViewModel 從 TodoScreen 提升狀態(tài)。 完成后干花,我們將創(chuàng)建一個(gè)單向數(shù)據(jù)流設(shè)計(jì)

viemodel-todoscreen.png

TodoScreen 集成到 TodoActivity.kt

class TodoActivity : AppCompatActivity() {

    val todoViewModel by viewModels<TodoViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            StateCodelabTheme {
                Surface {
                    TodoActivityScreen(todoViewModel)
                }
            }
        }
    }
}

@Composable
private fun TodoActivityScreen(todoViewModel: TodoViewModel) {
    TodoScreen(
        items = todoViewModel.todoItems,
        currentlyEditing = todoViewModel.currentEditItem,
        onAddItem = todoViewModel::addItem,
        onRemoveItem = todoViewModel::removeItem,
        onStartEdit = todoViewModel::onEditItemSelected,
        onEditItemChange = todoViewModel::onEditItemChange,
        onEditDone = todoViewModel::onEditDone
    )
}

Android Studio 在啟動(dòng)新 Compose 項(xiàng)目時(shí)創(chuàng)建的默認(rèn)主題妄帘。
Surface 為應(yīng)用程序添加背景,并配置文本顏色池凄。

Flow the events up 事件向上流動(dòng)

Kotlin 提示
您還可以使用方法引用語法生成一個(gè)調(diào)用單個(gè)方法的 lambda抡驼。 這將從方法調(diào)用中創(chuàng)建一個(gè) lambda。 使用方法引用語法肿仑,上面的 onAddItem 也可以表示為 onAddItem = todoViewModel::addItem致盟。

Pass the state down 向下傳遞狀態(tài)

現(xiàn)在我們已經(jīng)探索了如何使用 compose 和 ViewModels 來構(gòu)建單向數(shù)據(jù)流碎税,讓我們來探索 compose 如何在內(nèi)部與狀態(tài)交互。

有狀態(tài)的可組合是一種可以隨時(shí)間改變的狀態(tài)組合馏锡。

重新組合是再次運(yùn)行相同的組合以在其數(shù)據(jù)發(fā)生變化時(shí)更新樹的過程

Compose 生成一棵樹雷蹂,但它與您可能熟悉的 Android 視圖系統(tǒng)中的 UI 樹有點(diǎn)不同。 compose 生成了一棵可組合的樹杯道,而不是一棵 UI 小部件樹匪煌。

我們不希望每次 重新組合時(shí)都會(huì)改變。 為此党巾,我們需要一個(gè)地方來記住我們在上一個(gè)構(gòu)圖中使用的元素萎庭。 Compose 允許我們將值存儲在組合樹中,因此我們可以更新相應(yīng)的操作 以將【值或者狀態(tài)】 存儲在組合樹中齿拂。

每次 重構(gòu)時(shí)一些元素都會(huì)更新的原因是 有一個(gè)隱藏的副作用驳规。 副作用是在可組合函數(shù)的執(zhí)行之外可見的任何更改。

Remember

remember 給出了一個(gè)可組合的函數(shù)內(nèi)存创肥。

由記住計(jì)算的值將存儲在組合樹中达舒,并且只有在要記住的鍵發(fā)生變化時(shí)才會(huì)重新計(jì)算值朋。

您可以將 memory 視為將單個(gè)對象的存儲空間分配給函數(shù)叹侄,就像私有 val 屬性在對象中所做的那樣。

可組合函數(shù)可以使用 remember 可組合項(xiàng)記住單個(gè)對象昨登。系統(tǒng)會(huì)在初始組合期間將由 remember 計(jì)算的值存儲在組合中趾代,并在重組期間返回存儲的值。remember 既可用于存儲可變對象丰辣,又可用于存儲不可變對象撒强。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者笙什。
  • 序言:七十年代末飘哨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子琐凭,更是在濱河造成了極大的恐慌芽隆,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件统屈,死亡現(xiàn)場離奇詭異胚吁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)愁憔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門腕扶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吨掌,你說我怎么就攤上這事半抱∨。” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵窿侈,是天一觀的道長进肯。 經(jīng)常有香客問我,道長棉磨,這世上最難降的妖魔是什么江掩? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮乘瓤,結(jié)果婚禮上环形,老公的妹妹穿的比我還像新娘。我一直安慰自己衙傀,他們只是感情好抬吟,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著统抬,像睡著了一般火本。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上聪建,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天钙畔,我揣著相機(jī)與錄音,去河邊找鬼金麸。 笑死擎析,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的挥下。 我是一名探鬼主播揍魂,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棚瘟!你這毒婦竟也來了现斋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤偎蘸,失蹤者是張志新(化名)和其女友劉穎庄蹋,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體禀苦,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔓肯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了振乏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔗包。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖慧邮,靈堂內(nèi)的尸體忽然破棺而出调限,到底是詐尸還是另有隱情舟陆,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布耻矮,位于F島的核電站秦躯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏裆装。R本人自食惡果不足惜踱承,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哨免。 院中可真熱鬧茎活,春花似錦、人聲如沸琢唾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽采桃。三九已至懒熙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間普办,已是汗流浹背工扎。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泌豆,地道東北人定庵。 一個(gè)月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像踪危,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子猪落,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容