试着用Compose做个抽签工具

起因
上一篇文章提到了viewModel在compose中的使用,并且我们设置了界面状态类和界面意图类,并且viewModel中还需要监听意图,每一个都这么写比较麻烦,因此我们这次来做一个基类,统一接口。
开一个小坑APP
美味选择器

做这个是因为最近吃饭都在纠结吃什么,并且刚好之前有说compose,这次就想着用compose做一下。
这次采用的是compose1.3.1版本,事实上和上次的版本一样,只不过这次换md3风格了。
功能分析
我们需要下面的功能
提供一个食物列表,可以让用户添加和删除食物。
提供一个随机抽取食物的功能,并且要体现这个抽奖过程,也就是让用户知道,随机抽取过程,有哪些食物被经过和最终选择了哪个食物。
提供一个突出显示,最终被抽取的是哪个食物。
界面分析

这个界面很简单,就是一个顶部,底部导航栏,和头部的卡片,底下的瀑布流布局。
还记得上一次吗?我们可以利用脚手架快速完成大致的内容布置。
界面实现
下面是大致内容的布局,可以不在意mainViewModel
这里我们需要注意一下,我们里边有个appBarHeight,没错,当我们使用了topAppBar后,Scaffold的content的内容会被topAppBar遮挡,为此,我们需要计算出topAppBar的实际高度,并且为content添加一个边距,使得content的内容始终在topAppBar下面。
同时,这一次我们使用了一个BottomAppBar,这是底部应用栏,上一次我们并没有填充这个控件,而这一次在MD3的风格下我们使用了BottomAppBar。
事实上我们看到了HomeContent(),但是没有发现具体代码,下面我们看看HomeContent()的代码。
其中initFoodList和initAddFoodDialog分别是加载食物列表的意图发送和添加食物的对话框。
可以看到这里还有LazyVerticalStaggeredGrid,是我们上一次已经使用过的瀑布流布局了,我感觉如果每条的视觉一样会没有那种感觉,因此,这里我尝试了瀑布流,但效果不是很好。
这次,我们通过editFoodListState来确定这个布局是否处于编辑状态,再通过selectFoodInfo的id进行比对,当前抽签抽到这条item了没有。
在item的布局上使用了Chip而并非Card,因为我需要Chip的点击状态来使得这个抽取食物的过程更加明显。
布局总结之Compose在这个需求中的优势与劣势。
我们从这个需求来说,重点就在这个抽取过程中的item外观改变。
问题:对于这个食物被抽取的过程要体现出来,就要代表食物的item显示状态要改变,比如颜色加深,以及启动编辑时item的显示也得改变,使得用户知道当前是编辑状态。
Compose:这个需求对于Compose来说是比较好解决的,事实上我们通过不同的UI状态,判断展示哪个item组件。
XML:第一种办法,我们可以考虑采用多个item布局和类型配合,adapter判断是哪种类型就显示哪个item,当抽取时。第二种办法,我们可以考虑采用viewbinding等,直接在xml上做手脚。
个人观点:这方面来说,我认为Compose实现起来要好很多,并且刷新数据不需要太多行为,也不需要那么多布局,和判断后再做bind绑定等行为,这有些麻烦。但缺点也有,adapter可以使得item变动有动画,尽管Compose也可以做到,但是绝对不如adapter那样方便。这使得Compose的视觉体验不怎么样(我还没有用过Compose的动画,不清楚怎么实现)
到这里我们的布局就实现完成了,现在看看viewmodel。
ViewModel实现
废话不多说,我们直接上代码。
还记得上一次吗?我们采用了MVI的设计模式,这里我们也是如此,并且我也比较喜欢这种。
我们先看看Intent(意图)
以及State(状态)
这次的程序目前功能也比较单一,因此状态不是那么多。
最后我们看看ViewModel
这里的代码和上次的差不多,但是我要说的是extractFood这个方法,我们开启一个协程,重复执行二十次里边的代码,里边会产生一个0到food数量-1之间的随机数,用来代表这一次随机到了哪个食物,并且将这个food对象设置给selectFoodInfo,当20次执行结束后,会更新selectFoodState状态,告诉UI,你改把暂停按钮转化为播放按钮了,因为这次抽取已经结束了。
至于其他方法,大多数是添加一个食物,刷新列表内容,或者设置状态值,通过状态改动让UI发生变化。
特殊
我们发现这个类实现了handleEvent这个方法,并且继承了一个类ComposeBaseViewModel。
欸?上一次使用的管道,状态变量在这个类都没有了,是的,我们把一些方法进行了抽离在ComposeBaseViewModel实现了一部分,方便我们后续开发。
ComposeBaseViewModel
先看看这个类吧
这个类也比较简单,它实现了一个接口IViewModelHandle,这个就是handleEvent这个方法的来源。
同时,我们发现管道,UI状态都在这里被定义了,同时管道的数据收集也在这里,这里我们还定义了一个方法sendIntent,这个方法用来发送意图,我们之前是需要viewMode再找到管道,再调用send方法,现在我们就直接调用封装好的方法即可。
这样,我们在使用时只需要传递泛型和状态的对象,就可以简化一些东西,当然你还可以做的更好。
最后
很感谢大家看到这里,如果有说的不对,请在评论区指出。
项目开源地址:
https://github.com/1250422131/DelicacySelector
有兴趣了打包看看