試著用Compose做個抽簽工具

起因
上一篇文章提到了viewModel在compose中的使用,并且我們設(shè)置了界面狀態(tài)類和界面意圖類,并且viewModel中還需要監(jiān)聽意圖,每一個都這么寫比較麻煩,因此我們這次來做一個基類,統(tǒng)一接口。
開一個小坑APP
美味選擇器

做這個是因為最近吃飯都在糾結(jié)吃什么,并且剛好之前有說compose,這次就想著用compose做一下。
這次采用的是compose1.3.1版本,事實上和上次的版本一樣,只不過這次換md3風(fēng)格了。
功能分析
我們需要下面的功能
提供一個食物列表,可以讓用戶添加和刪除食物。
提供一個隨機(jī)抽取食物的功能,并且要體現(xiàn)這個抽獎過程,也就是讓用戶知道,隨機(jī)抽取過程,有哪些食物被經(jīng)過和最終選擇了哪個食物。
提供一個突出顯示,最終被抽取的是哪個食物。
界面分析

這個界面很簡單,就是一個頂部,底部導(dǎo)航欄,和頭部的卡片,底下的瀑布流布局。
還記得上一次嗎?我們可以利用腳手架快速完成大致的內(nèi)容布置。
界面實現(xiàn)
下面是大致內(nèi)容的布局,可以不在意mainViewModel
這里我們需要注意一下,我們里邊有個appBarHeight,沒錯,當(dāng)我們使用了topAppBar后,Scaffold的content的內(nèi)容會被topAppBar遮擋,為此,我們需要計算出topAppBar的實際高度,并且為content添加一個邊距,使得content的內(nèi)容始終在topAppBar下面。
同時,這一次我們使用了一個BottomAppBar,這是底部應(yīng)用欄,上一次我們并沒有填充這個控件,而這一次在MD3的風(fēng)格下我們使用了BottomAppBar。
事實上我們看到了HomeContent(),但是沒有發(fā)現(xiàn)具體代碼,下面我們看看HomeContent()的代碼。
其中initFoodList和initAddFoodDialog分別是加載食物列表的意圖發(fā)送和添加食物的對話框。
可以看到這里還有LazyVerticalStaggeredGrid,是我們上一次已經(jīng)使用過的瀑布流布局了,我感覺如果每條的視覺一樣會沒有那種感覺,因此,這里我嘗試了瀑布流,但效果不是很好。
這次,我們通過editFoodListState來確定這個布局是否處于編輯狀態(tài),再通過selectFoodInfo的id進(jìn)行比對,當(dāng)前抽簽抽到這條item了沒有。
在item的布局上使用了Chip而并非Card,因為我需要Chip的點擊狀態(tài)來使得這個抽取食物的過程更加明顯。
布局總結(jié)之Compose在這個需求中的優(yōu)勢與劣勢。
我們從這個需求來說,重點就在這個抽取過程中的item外觀改變。
問題:對于這個食物被抽取的過程要體現(xiàn)出來,就要代表食物的item顯示狀態(tài)要改變,比如顏色加深,以及啟動編輯時item的顯示也得改變,使得用戶知道當(dāng)前是編輯狀態(tài)。
Compose:這個需求對于Compose來說是比較好解決的,事實上我們通過不同的UI狀態(tài),判斷展示哪個item組件。
XML:第一種辦法,我們可以考慮采用多個item布局和類型配合,adapter判斷是哪種類型就顯示哪個item,當(dāng)抽取時。第二種辦法,我們可以考慮采用viewbinding等,直接在xml上做手腳。
個人觀點:這方面來說,我認(rèn)為Compose實現(xiàn)起來要好很多,并且刷新數(shù)據(jù)不需要太多行為,也不需要那么多布局,和判斷后再做bind綁定等行為,這有些麻煩。但缺點也有,adapter可以使得item變動有動畫,盡管Compose也可以做到,但是絕對不如adapter那樣方便。這使得Compose的視覺體驗不怎么樣(我還沒有用過Compose的動畫,不清楚怎么實現(xiàn))
到這里我們的布局就實現(xiàn)完成了,現(xiàn)在看看viewmodel。
ViewModel實現(xiàn)
廢話不多說,我們直接上代碼。
還記得上一次嗎?我們采用了MVI的設(shè)計模式,這里我們也是如此,并且我也比較喜歡這種。
我們先看看Intent(意圖)
以及State(狀態(tài))
這次的程序目前功能也比較單一,因此狀態(tài)不是那么多。
最后我們看看ViewModel
這里的代碼和上次的差不多,但是我要說的是extractFood這個方法,我們開啟一個協(xié)程,重復(fù)執(zhí)行二十次里邊的代碼,里邊會產(chǎn)生一個0到food數(shù)量-1之間的隨機(jī)數(shù),用來代表這一次隨機(jī)到了哪個食物,并且將這個food對象設(shè)置給selectFoodInfo,當(dāng)20次執(zhí)行結(jié)束后,會更新selectFoodState狀態(tài),告訴UI,你改把暫停按鈕轉(zhuǎn)化為播放按鈕了,因為這次抽取已經(jīng)結(jié)束了。
至于其他方法,大多數(shù)是添加一個食物,刷新列表內(nèi)容,或者設(shè)置狀態(tài)值,通過狀態(tài)改動讓UI發(fā)生變化。
特殊
我們發(fā)現(xiàn)這個類實現(xiàn)了handleEvent這個方法,并且繼承了一個類ComposeBaseViewModel。
欸?上一次使用的管道,狀態(tài)變量在這個類都沒有了,是的,我們把一些方法進(jìn)行了抽離在ComposeBaseViewModel實現(xiàn)了一部分,方便我們后續(xù)開發(fā)。
ComposeBaseViewModel
先看看這個類吧
這個類也比較簡單,它實現(xiàn)了一個接口IViewModelHandle,這個就是handleEvent這個方法的來源。
同時,我們發(fā)現(xiàn)管道,UI狀態(tài)都在這里被定義了,同時管道的數(shù)據(jù)收集也在這里,這里我們還定義了一個方法sendIntent,這個方法用來發(fā)送意圖,我們之前是需要viewMode再找到管道,再調(diào)用send方法,現(xiàn)在我們就直接調(diào)用封裝好的方法即可。
這樣,我們在使用時只需要傳遞泛型和狀態(tài)的對象,就可以簡化一些東西,當(dāng)然你還可以做的更好。
最后
很感謝大家看到這里,如果有說的不對,請在評論區(qū)指出。
項目開源地址:
https://github.com/1250422131/DelicacySelector
有興趣了打包看看