Android Jetpack Compose 使用 ViewModel

1.ViewModel为啥不可或缺

我们已经了解到了rememberSavable可以在屏幕旋转,当前Activity被系统回收时保存状态。ViewModel正好也是干这个活的,那为啥没有使用rememberSavable替换ViewModel呢?比如我们在前面的文章中用到的计数器例子,可以使用remremberSavable来保存状态,也可以用viewModel来保存状态,但是这只是一个Demo而已,在真实的项目中,业务逻辑是不会只是简单的加加减减,往往会复杂得多得多,如果将代码全放到Stateful Composable中,会导致UI的职责不清晰,毕竟我们的Composable的主要职责是显示UI。

所以,在复杂的业务逻辑下,我们可以将Stateful的状态提到ViewModel中管理,这样Stateful Composable也就变成了一个Stateless Composable,通过参数传入不同的ViewModel即可替换具体的业务逻辑,大大增加了可复用性和可测试性

2 在Compose UI中使用ViewModel

借用前面的计数器例子,这里使用Compose UI 和ViewModel的配合,实现一个计数器例子,代码如下:

kotlin 复制代码
class ComposeCounterAct : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyComposeTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    TestCounter()
                }
            }
        }
    }

    @Composable
    fun TestCounter(){
        val viewModel:ComposeCounterViewModel = viewModel()
        CounterComponent(viewModel.counter.value,viewModel::increment,viewModel::decrement)
    }

    @Composable
    fun CounterComponent(
        counter: Int, // 重组时传入当前需要显示的计数
        onIncrement: () -> Unit,// 回调点击加号的事件
        onDecrement: () -> Unit // 回调单击减号的事件
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                "$counter",
                Modifier.fillMaxWidth(),
                textAlign = TextAlign.Center
            )

            Row {
                Button(
                    onClick = { onDecrement() },
                    modifier = Modifier.weight(1f)
                ) {
                    Text("-")
                }

                Spacer(Modifier.width(16.dp))
                Button(
                    onClick = { onIncrement() },
                    modifier = Modifier.weight(1f)
                ) {
                    Text("+")
                }
            }
        }
    }
}

class ComposeCounterViewModel:ViewModel(){
    private val _counter = mutableStateOf(0)
    val counter: State<Int> = _counter

    fun increment(){
        _counter.value = _counter.value + 1
    }

    fun decrement(){
      if(_counter.value>0){
          _counter.value = _counter.value -1
      }
    }
}

如上面代码所示,在Compose UI 对于ViewModel的使用和传统的view基本相同,在Compose中viewModel()方法是一个Composable方法,它的作用是在Composable中创建ViewModel,TestCounter通过viewModel()方法创建了一个ComposeCounterViewModel类型的ViewModel,这个ViewModel持有计数器的状态。

注意:在Composable中使用viewModel()方法需要添加依赖: implementation('androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2') ,否则会提示方法找不到。

这里的viewModel()方法会从当前最近的ViewModelStore中获取ViewModel实例,这个ViewModelStore可能是一个Activity,也可能是一个Fragment,如果ViewModel实例不存在,就创建一个新的并且存入ViewModelStore。只要是ViewModelStore不销毁,其内部的ViewModel实例就会一直存活。例如一个Activity中的Composable通过viewModel()方法创建的ViewModel被当前的Activity所持有。在Activity销毁之前,ViewModel会一直存在,viewModel()的每次调用都会返回一个实例,所以我们不用remember缓存也可以。

注意: 调用viewModel()方法的Composable无法进行预览,若需要进行预览,可以从持有ViewModel的Composable中将需要预览的部分提取成StateLess组件,如文中的CounterComponent组件。

相关推荐
hai_android1 小时前
从零实现一个支持跨进程(IPC)的自定义 Toast
android·kotlin·android jetpack
hai_android3 天前
LiveEventBusCore 跨 Activity 通讯源码解析
android·kotlin·android jetpack
我命由我123456 天前
Android 广播 - 组件通信
android·java·开发语言·android studio·android jetpack·android-studio·android runtime
alexhilton7 天前
为什么你的 LazyColumn 仍然卡顿
android·kotlin·android jetpack
hai_android7 天前
Android 嵌套滑动冲突:内部拦截 vs 外部拦截实战
android·android studio·android jetpack
alexhilton11 天前
模块化Jetpack Compose架构
android·kotlin·android jetpack
hai_android11 天前
InputStage 责任链核心执行流程
android·android studio·android jetpack
传奇开心果编程12 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程13 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程13 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack