如何使用 Jetpack Compose 强制 UI 重新加载?

问题描述 投票:0回答:1

我有一个 GameState 对象,只要游戏状态发生变化,它就会更新,并且某些 UI 无法正确更新。我想在数据重新加载时调用重新加载函数,这样我就不必担心它不更新。

package dev.madcatter.lostworld

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import dev.madcatter.lostworld.data.GameState
import dev.madcatter.lostworld.ui.Console
import dev.madcatter.lostworld.ui.Turns
import dev.madcatter.lostworld.ui.theme.LostWorldTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class MainActivity : ComponentActivity() {

    private lateinit var myApp: LostWorldApp
    private lateinit var gameState: MutableState<GameState>
    private var updateTicker: MutableState<Int> = mutableStateOf(0)

    @OptIn(ExperimentalMaterial3Api::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        myApp = application as? LostWorldApp ?: throw IllegalStateException("Application class is not LostWorldApp")
        gameState = mutableStateOf(myApp.gameState!!)

        enableEdgeToEdge()
        setContent {
            LostWorldTheme {
                MainContent()
            }
        }
    }

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    private fun MainContent() {
        Scaffold(
            topBar = {
                TopAppBar(
                    title = { Text(text = "Lost World") },
                    actions = {
                        IconButton(onClick = {
                            gameState.value.debug("---\n\nReload button clicked.")
                            reloadGameState()
                        }) {
                            Icon(
                                imageVector = Icons.Default.Refresh,
                                contentDescription = "Reload"
                            )
                        }
                        IconButton(onClick = {
                            startActivity(Intent(this@MainActivity, SettingsActivity::class.java))
                        }) {
                            Icon(
                                imageVector = Icons.Default.Settings,
                                contentDescription = "Settings"
                            )
                        }
                    }
                )
            },
            content = { innerPadding ->
                // Main content here
                Row(modifier = Modifier.padding(innerPadding)) {
                    Turns(
                        gameState.value,
                        modifier = Modifier
                            .padding(16.dp)
                            .weight(0.25f)
                            .fillMaxHeight()
                    )

                    VerticalDivider(
                        color = MaterialTheme.colorScheme.outline,
                        modifier = Modifier
                            .width(1.dp)
                            .align(Alignment.CenterVertically)
                            .fillMaxHeight(0.85f)
                    )

                    Console(
                        gameState.value,
                        modifier = Modifier
                            .padding(16.dp)
                            .weight(0.75f)
                            .fillMaxHeight()
                    )
                }
            }
        )
    }

    override fun onStart() {
        super.onStart()

        // Run the reload after a short delay using lifecycleScope
        lifecycleScope.launch(Dispatchers.Main) {
            delay(200) // Wait 200 milliseconds, or adjust as necessary
            reloadGameState()
        }
    }

    private fun reloadGameState() {
        gameState.value.reload()
        gameState.value = myApp.gameState!!
        updateTicker.value++
    }
}

我尝试过的:

  • 每当 Activity 到达前台时,我都会使用 onStart() 重新加载 gameState,添加一个小的延迟以使 UI 稳定下来。
  • 为了刷新 UI,我重新分配了 gameState,这有时会导致输出被清除。
  • 重新分配 gameState 会导致清除状态问题,例如丢失控制台输出。
  • 现在我正在探索如何最好地仅更新 gameState 的必要属性以触发 UI 刷新而不丢失现有数据。
android android-jetpack-compose
1个回答
0
投票

使用 jetpack compose 时,切勿手动更新 UI。它只会扼杀开发撰写应用程序的理由。我可以看到这是你的游戏对象。

gameState: MutableState<GameState>

如果您需要jetpack compose在对象状态发生变化时更新UI。您必须使用记住 API。因此,如果您像下面这样更改对象,您的 UI 应该会自动更新。

val state by remember{
mutableStateOf(myApp.gameState!!)
}

如果只是更新对象的特定属性并期望更新 UI,您可以使用复制功能使其工作。

state = state.copy(prop = ?)

我希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.