Jetpack Compose 在方向更改时保存状态

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

我正在使用 Android Jetpack 的 Compose,并且一直在尝试找出如何保存方向更改的状态。

我的思路是让一个类成为 ViewModel。因为当我使用 Android 的传统 API 时,这通常会起作用。

我使用 Remember {} 和 mutableState {} 在信息更改时更新 UI。 请验证我的理解是否正确...

remember = 保存变量并允许通过.value 访问,这允许缓存值。但它的主要用途是在更改时不重新分配变量。

mutableState = 当某些内容发生更改时更新变量。

许多博客文章都说使用@Model,但是,在尝试该方法时导入会出错。 所以,我添加了一个:ViewModel()

但是,我相信我的记忆 {} 正在阻止其按预期工作?

我能得到正确方向的一点吗?

@Composable
fun DefaultFlashCard() {

    val flashCards = remember { mutableStateOf(FlashCards())}
    

    Spacer(modifier = Modifier.height(30.dp))

    MaterialTheme {


        val typography = MaterialTheme.typography
        var question = remember { mutableStateOf(flashCards.value.currentFlashCards.question) }



        Column(modifier = Modifier.padding(30.dp).then(Modifier.fillMaxWidth())
                .then(Modifier.wrapContentSize(Alignment.Center))
                .clip(shape = RoundedCornerShape(16.dp))) {
            Box(modifier = Modifier.preferredSize(350.dp)
                    .border(width = 4.dp,
                            color = Gray,
                            shape = RoundedCornerShape(16.dp))
                    .clickable(
                            onClick = {
                                question.value = flashCards.value.currentFlashCards.answer })
                    .gravity(align = Alignment.CenterHorizontally),
                    shape = RoundedCornerShape(2.dp),
                    backgroundColor = DarkGray,
                    gravity = Alignment.Center) {
                Text("${question.value}",
                        style = typography.h4, textAlign = TextAlign.Center, color = White
                )
            }
        }

        Column(modifier = Modifier.padding(16.dp),
                horizontalGravity = Alignment.CenterHorizontally) {

            Text("Flash Card application",
                    style = typography.h6,
                    color = Black)

            Text("The following is a demonstration of using " +
                    "Android Compose to create a Flash Card",
                    style = typography.body2,
                    color = Black,
                    textAlign = TextAlign.Center)

            Spacer(modifier = Modifier.height(30.dp))
            Button(onClick = {
                flashCards.value.incrementQuestion();
                question.value = flashCards.value.currentFlashCards.question },
                    shape = RoundedCornerShape(10.dp),
                    content = { Text("Next Card") },
                    backgroundColor = Cyan)
        }
    }
}


data class Question(val question: String, val answer: String) {
}


class FlashCards: ViewModel() {

    var flashCards = mutableStateOf( listOf(
            Question("How many Bananas should go in a Smoothie?", "3 Bananas"),
            Question("How many Eggs does it take to make an Omellete?", "8 Eggs"),
            Question("How do you say Hello in Japenese?", "Konichiwa"),
            Question("What is Korea's currency?", "Won")
    ))

    var currentQuestion = 0

    val currentFlashCards
        get() = flashCards.value[currentQuestion]

    fun incrementQuestion() {
        if (currentQuestion + 1 >= flashCards.value.size) currentQuestion = 0 else currentQuestion++
    }
}

android kotlin android-jetpack android-viewmodel android-jetpack-compose
2个回答
18
投票

还有另一种方法可以处理 Compose 中的配置更改,它是

rememberSaveable
。正如文档所说

虽然

remember
可以帮助您在重组过程中保留状态,但在配置更改过程中不会保留状态。为此,您必须使用
rememberSaveable
rememberSaveable
自动保存可以保存在 Bundle 中的任何值。对于其他值,您可以传入自定义保护程序对象。

看起来穆罕默德的解决方案更稳健,但这个似乎更简单。


11
投票

更新:

Compose 有 2 种内置的方法来保存状态:

  1. remember
    :用于在 重组之间保存可组合函数中的状态。

  2. rememberSaveable
    remember
    重组处理配置更改进程死亡中保存状态,因此为了在配置更改和进程死亡中幸存下来,您应该使用
    rememberSaveable
    代替.

但是

rememberSaveable
也存在一些问题:

  1. 它支持开箱即用的原始类型,但对于更复杂的数据,例如

    data class
    ,您必须创建一个
    Saver
    来解释如何将状态持久保存到包中,

  2. rememberSaveable
    在底层使用了
    Bundle
    ,因此你可以保存的数据量是有限制的,如果数据太大,你将面临
    TransactionTooLarge
    异常。

如上所述,可以使用以下解决方案:

  1. android:configChanges

    中设置

    Manifest
    以避免配置更改中的活动重新创建。 (
    在进程死亡时没有用,也无法避免在 Android 12 中的壁纸更改中重新创建

  2. 使用
  3. ViewModel

    +

    remeberSaveable
    + 存储中数据持久化的组合
    
    

  4. ====================================================== ======

旧答案

与以前一样,您可以使用架构组件 ViewModel 来应对配置更改。

您应该在 Activity/Fragment 中初始化 ViewModel,然后将其传递给可组合函数。

class UserDetailFragment : Fragment() { private val viewModel: UserDetailViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return ComposeView(context = requireContext()).apply { setContent { AppTheme { UserDetailScreen( viewModel = viewModel ) } } } } }

然后你的 ViewModel 应该通过 LiveData 或 Flow 等方式公开 ViewState 

用户详细信息视图模型:

class UserDetailViewModel : ViewModel() { private val _userData = MutableLiveData<UserDetailViewState>() val userData: LiveData<UserDetailViewState> = _userData // or private val _state = MutableStateFlow<UserDetailViewState>() val state: StateFlow<UserDetailViewState> get() = _state }

现在您可以在可组合函数中观察这种状态:

@Composable fun UserDetailScreen( viewModel:UserDetailViewModel ) { val state by viewModel.userData.observeAsState() // or val viewState by viewModel.state.collectAsState() }

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