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的配合,实现一个计数器例子,代码如下:

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组件。

猜你喜欢

转载自blog.csdn.net/zxj2589/article/details/132850403