Android compose 使用viewModel

使用viewModel解耦UI与数据逻辑。先在build.gradle.kts中添加依赖viewModel:

Groovy 复制代码
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3")

但更建议在libs.versions.toml中定义版本:

然后,在build.gradle.kts中添加依赖:

再自定义一个viewModel类:

Kotlin 复制代码
package com.example.testcompose1

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class TodoViewModel : ViewModel() {
    // MutableStateFlow 为可修改的状态流
    // 使用 StateFlow 持有待办列表,这样Compose可以观察
    private val _todoItems = MutableStateFlow<List<String>>(emptyList())
    // StateFlow 是MutableStateFlow的父接口, 只读。 即对外暴露这个只读的
    val todoItems: StateFlow<List<String>> = _todoItems.asStateFlow()

    fun addItem(item: String) {
        if (item.isNotBlank()) {
            _todoItems.value += item
        }
    }

    fun removeItem(item: String) {
        _todoItems.value -= item
    }
}

mainActivity中,使用viewModel,主要是把数据和回调事件都替换成viewModel的,完整代码:

Kotlin 复制代码
package com.example.testcompose1

import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextField
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            todoJobList()
        }
    }
}

@Preview
@Composable
fun todoJobList(
    viewModel: TodoViewModel = viewModel()  // 获取ViewModel实例。 在同一个activity作用域中是单例。
) {
    val context = LocalContext.current

    // 使用 remember 和 mutableStateOf 保存输入框的文本
    var text by remember { mutableStateOf("") }
    // 使用 mutableStateListOf 保存待办项列表
//    val todoItems = remember { mutableStateListOf<String>() }
    // 将 StateFlow 转换为 Compose 可观察的 State
    val todoItems by viewModel.todoItems.collectAsState()

    Column(modifier = Modifier.padding(16.dp)) {
        // 文本输入框
        TextField(
            value = text,
            onValueChange = { text = it }, // 反向绑定,视图变化--> 数据变化
            label = { Text("输入待办事项") },
            modifier = Modifier.fillMaxWidth()
        )
        // 添加按钮
        Button(
            onClick = {
                viewModel.addItem(text)
                text = ""
            },
            modifier = Modifier.padding(top = 8.dp)
        ) {
            Text("添加")
        }

        // 显示待办列表
        Spacer(modifier = Modifier.height(16.dp))
        Text("待办列表", style = MaterialTheme.typography.titleMedium)
        LazyColumn {
            items(items = todoItems) { item ->
                TodoItem(item = item
                    , onDelete = { viewModel.removeItem(item) })
            }
        }
    }
}

@Composable
fun TodoItem(item: String, onDelete: () -> Unit  // 添加删除回调,删除逻辑放在上层。即把回调传给里面的按钮。
 ) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 8.dp),
        horizontalArrangement = Arrangement.SpaceBetween // 横向布局子元素两端对齐,剩余空白空间平均分配到子元素之间
    ) {
        Text(text = item)
        IconButton(onClick = onDelete) {
            Icon(Icons.Default.Delete, contentDescription = "删除")
        }
    }
}

测试下:

ok. ViewModel中加入这段代码模拟加载数据:

Kotlin 复制代码
init {
        viewModelScope.launch {
            // 模拟加载数据
            delay(2000)
            _todoItems.value = listOf("人中吕布,马中赤兔", "我堂堂吕布怎么成了三姓家奴", "公不见丁原董卓之事乎")
        }
    }

测试下:

ok.

相关推荐
DogDaoDao21 小时前
Android 硬件编码器参数完全指南:MediaCodec 深度解析
android·音视频·视频编解码·h264·硬编码·视频直播·mediacodec
JohnnyDeng941 天前
Android 自定义 View:Canvas 绘图与事件分发深度解析
android
Android小码家1 天前
Framework之Launcher小窗开发
android·framework·虚拟屏·小窗
赏金术士1 天前
第七章:状态管理实战与架构总结
android·ui·kotlin·compose
颂love1 天前
MySQL的执行流程
android·数据库·mysql
云起SAAS1 天前
抖音小游戏源码 - 消消乐 | 含激励广告+成就系统 | 开箱即用商业级消除游戏模板
android·游戏·广告联盟·看激励广告联盟流量主·抖音小游戏源码 - 消消乐
大貔貅喝啤酒1 天前
基于Windows下载安装Android Studio 3.3.2版本教程(2026详细图文版)
android·java·windows·android studio
程序员码歌1 天前
OpenSpec 到 Superpowers:AI 编码从说清到做对
android·前端·人工智能
2501_915106321 天前
深入解析无源码iOS加固原理与方案,保护应用安全
android·安全·ios·小程序·uni-app·cocoa·iphone
黄林晴2 天前
重磅官宣:Android UI 开发正式进入 Compose-first 时代
android·google io