You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何持久化保存用户输入的山羊信息?

山羊数据持久化实现方案

要实现应用重启后保留山羊数据,你需要完成以下核心改造:

一、调整数据模型支持序列化

你的Goats类需要能被序列化,才能保存到本地存储。同时建议把姓名、品种从字符串资源ID改为普通字符串,适配用户自定义输入:

data class Goats(
    val name: String,
    val breed: String,
    val age: Int,
    val additionalInfo: String = "",
    val imageUri: String = ""
) : Serializable

二、改造ViewModel管理数据与持久化

把数据从Composable的remember移到ViewModel,用SharedPreferences作为入门级持久化方案(适合小体量数据):

class MainViewModel : ViewModel() {
    private val sharedPrefs = ApplicationProvider.getApplicationContext<Context>().getSharedPreferences("GoatsPrefs", Context.MODE_PRIVATE)
    val goats = mutableStateListOf<Goats>()
    val bucks = mutableStateListOf<Goats>()
    var isDialogShown by mutableStateOf(false)

    init {
        loadSavedData() // 启动时加载本地数据
    }

    // 保存数据到本地
    private fun saveData() {
        val editor = sharedPrefs.edit()
        val gson = Gson()
        // 序列化山羊列表
        editor.putString("goats", gson.toJson(goats))
        editor.putString("bucks", gson.toJson(bucks))
        editor.apply()
    }

    // 从本地加载数据
    private fun loadSavedData() {
        val gson = Gson()
        val goatsJson = sharedPrefs.getString("goats", null)
        val bucksJson = sharedPrefs.getString("bucks", null)

        // 加载Does数据,没有则初始化默认值
        goatsJson?.let {
            val type = object : TypeToken<MutableList<Goats>>() {}.type
            goats.addAll(gson.fromJson(it, type))
        } ?: run {
            goats.addAll(listOf(
                Goats("Red", "Alpine", 7),
                Goats("Little Goat", "Alpine", 8),
                Goats("Nana", "Alpine", 7),
                Goats("Sharkie", "Alpine", 10)
            ))
        }

        // 加载Bucks数据,没有则初始化默认值
        bucksJson?.let {
            val type = object : TypeToken<MutableList<Goats>>() {}.type
            bucks.addAll(gson.fromJson(it, type))
        } ?: run {
            bucks.add(Goats("Billy", "Mix", 7))
        }
    }

    // 添加新山羊并保存
    fun addGoat(goat: Goats) {
        goats.add(goat)
        saveData()
    }

    fun addBuck(buck: Goats) {
        bucks.add(buck)
        saveData()
    }

    // 更新山羊信息并保存
    fun updateGoat(index: Int, updatedGoat: Goats) {
        if (index in goats.indices) {
            goats[index] = updatedGoat
            saveData()
        }
    }

    fun updateBuck(index: Int, updatedBuck: Goats) {
        if (index in bucks.indices) {
            bucks[index] = updatedBuck
            saveData()
        }
    }

    fun isGoatClick() {
        isDialogShown = true
    }

    fun onDismissDialog() {
        isDialogShown = false
    }
}

需在Module的build.gradle中添加Gson依赖:implementation 'com.google.code.gson:gson:2.10.1'

三、修改Composable代码,绑定ViewModel数据

1. MainActivity中正确获取ViewModel实例

class MainActivity : ComponentActivity() {
    private val viewModel by viewModels<MainViewModel>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            GoatsTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    GoatsApp(viewModel = viewModel)
                }
            }
        }
    }
}

2. 修改GoatsApp,使用ViewModel的列表数据

@Composable
fun GoatsApp(viewModel: MainViewModel) {
    val provider = GoogleFont.Provider(
        providerAuthority = "com.google.android.gms.fonts",
        providerPackage = "com.google.android.gms",
        certificates = R.array.com_google_android_gms_fonts_certs
    )
    val fontName = GoogleFont("Lato")
    val fontFamily = FontFamily(Font(googleFont = fontName, fontProvider = provider))

    Column(modifier = Modifier
        .padding(16.dp)
        .fillMaxHeight(0.90f)) {
        // Does区域
        Row(
            modifier = Modifier
                .fillMaxWidth(0.95f)
                .border(3.dp, Color.Gray, RoundedCornerShape(13.dp))
        ) {
            Button(
                onClick = { viewModel.addGoat(Goats("Little Goat", "Alpine", 9)) },
                shape = RoundedCornerShape(10.dp),
                modifier = Modifier.background(Color.LightGray)
            ) { Text(text = "+") }
            Text(
                text = "Does",
                modifier = Modifier
                    .background(Color.LightGray)
                    .fillMaxWidth()
                    .padding(12.dp),
                fontStyle = FontStyle.Italic,
                textAlign = TextAlign.Center,
                fontFamily = fontFamily,
                fontWeight = FontWeight.ExtraLight
            )
        }
        GoatList(modifier = Modifier.padding(8.dp), goats = viewModel.goats, viewModel = viewModel)

        // Bucks区域
        Row(
            modifier = Modifier
                .fillMaxWidth(0.95f)
                .border(3.dp, Color.Gray, RoundedCornerShape(13.dp))
        ) {
            Button(
                onClick = { viewModel.addBuck(Goats("Billy", "Mix", 9)) },
                shape = RoundedCornerShape(10.dp),
                modifier = Modifier.background(Color.LightGray)
            ) { Text(text = "+") }
            Text(
                text = "Bucks",
                modifier = Modifier
                    .background(Color.LightGray)
                    .fillMaxWidth()
                    .padding(12.dp),
                fontStyle = FontStyle.Italic,
                textAlign = TextAlign.Center,
                fontFamily = fontFamily,
                fontWeight = FontWeight.ExtraLight
            )
        }
        BuckList(modifier = Modifier.padding(8.dp), bucks = viewModel.bucks, viewModel = viewModel)
    }
}

3. 改造列表组件,传递索引和ViewModel

@Composable
fun GoatList(modifier: Modifier = Modifier, goats: List<Goats>, viewModel: MainViewModel) {
    LazyVerticalGrid(columns = GridCells.Fixed(1), modifier.padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
        itemsIndexed(goats) { index, goat ->
            GoatCard(allGoats = goat, index = index, isBuck = false, viewModel = viewModel)
        }
    }
}

@Composable
fun BuckList(modifier: Modifier = Modifier, bucks: List<Goats>, viewModel: MainViewModel) {
    LazyVerticalGrid(columns = GridCells.Fixed(1), modifier.padding(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
        itemsIndexed(bucks) { index, buck ->
            GoatCard(allGoats = buck, index = index, isBuck = true, viewModel = viewModel)
        }
    }
}

4. 修改GoatCard,弹窗关闭时更新数据

@Composable
fun GoatCard(allGoats: Goats, index: Int, isBuck: Boolean, viewModel: MainViewModel) {
    var stateAdditional by rememberSaveable { mutableStateOf(allGoats.additionalInfo) }
    var stateName by rememberSaveable { mutableStateOf(allGoats.name) }
    var stateAge by rememberSaveable { mutableStateOf(allGoats.age.toString()) }
    val imageUri = rememberSaveable { mutableStateOf(allGoats.imageUri) }

    val myImage = rememberAsyncImagePainter(
        imageUri.value.ifEmpty { R.drawable.pexels_anna_shvets_5953826 }
    )

    val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.GetContent()) { uri: Uri? ->
        uri?.let { imageUri.value = it.toString() }
    }

    Card(modifier = Modifier) {
        Row {
            Image(
                painter = myImage,
                contentDescription = stateName,
                modifier = Modifier.size(68.dp),
                contentScale = ContentScale.Crop
            )

            Column(Modifier.width(120.dp)) {
                Text(
                    text = stateName,
                    modifier = Modifier.padding(8.dp),
                    style = MaterialTheme.typography.titleSmall
                )
                Row {
                    Text(
                        text = allGoats.breed,
                        modifier = Modifier.padding(8.dp),
                        style = MaterialTheme.typography.bodySmall
                    )
                    Text(
                        stateAge,
                        modifier = Modifier.padding(8.dp),
                        style = MaterialTheme.typography.bodySmall
                    )

                    Box {
                        Button(
                            onClick = { viewModel.isGoatClick() },
                            modifier = Modifier.fillMaxWidth(),
                            colors = ButtonDefaults.buttonColors(Color.LightGray)
                        ) {}
                        Text(
                            text = "More",
                            style = MaterialTheme.typography.bodySmall,
                            textAlign = TextAlign.End,
                            fontWeight = FontWeight.Bold,
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(end = 6.dp, top = 12.dp)
                        )
                    }
                }

                if (viewModel.isDialogShown) {
                    CustomDialog(
                        onDismiss = {
                            viewModel.onDismissDialog()
                            // 解析年龄并更新数据
                            val age = stateAge.toIntOrNull() ?: allGoats.age
                            val updatedGoat = allGoats.copy(
                                name = stateName,
                                age = age,
                                additionalInfo = stateAdditional,
                                imageUri = imageUri.value
                            )
                            if (isBuck) viewModel.updateBuck(index, updatedGoat)
                            else viewModel.updateGoat(index, updatedGoat)
                        },
                        myGoats = allGoats,
                        onValueChange = { stateName = it },
                        value = stateName,
                        onAgeValueChange = { stateAge = it },
                        ageValue = stateAge,
                        launcher = launcher,
                        myImage = myImage,
                        addValue = stateAdditional,
                        addOnValueChange = { stateAdditional = it },
                    )
                }
            }
        }
    }
}

四、进阶方案:Room数据库

如果后续数据量增大或需要复杂查询,推荐使用Android官方的Room数据库,它更适合结构化数据的持久化。可以先掌握SharedPreferences方案,再逐步过渡。


内容的提问来源于stack exchange,提问作者vukica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 07:52:04