kotlin 喷气背包组合:当状态从另一个活动更改时强制重新组合

abithluo  于 2022-11-30  发布在  Kotlin
关注(0)|答案(1)|浏览(124)

我有两项活动:ActivityOne.ktActivityTwo.kt。它们都使用Jetpack Compose来显示UI。在第一个示例中,必须显示一个项目(比如Text),变量showtTexttrue,如果为false,则隐藏该项目。我使用以下代码来实现:

@Composable
fun MyUI(){   
 AnimatedVisibility(visible = viewModel.showText) {
      Text("Some text")      
   }
}

我的变量showText是在ViewModel中定义的,例如:

val showText by mutableStateOf(false)

这样,当ActivityOne.kt可见时,我在ViewModel中更改showText的值时,它会出现或消失。
1.用户可以从ActivityOne.kt导航到ActivityTwo.kt(第一个在后台运行,不会从堆栈中清除)。
1.这里有一个Switch可以切换showText的值。
1.当用户按下“返回”时,ActivityTwo.kt调用finish(),它消失了,再次显示ActivityOne.kt(它在堆栈中)。
1.如果用户已在ActivityTwo.kt中切换showText值,则文本应自动隐藏或显示,因为showText的状态已更改。
问题是,尽管showText的值确实发生了变化,但ActivityOne.kt中的UI并没有对这些变化做出响应。我检查了ActivityOne.kt的UI在ActivityTwo.kt完成后没有发生重组,因为它没有记住showText的先前状态。
我怎么能做到呢?提前感谢!

EDIT 1这个问题比我解释的要难一些,但是基础是一样的。下面是我的完整代码:

  • 练习一.kt:*
class ActivityOne : ComponentActivity() {

    private lateinit var vm: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val vmFactory = MainViewModelFactory()
        vm = ViewModelProvider(this, vmFactory).get(MainViewModel::class.java)
        setContent {
            MyTheme {
                Surface {
                    MyUI()
                }
            }
        }
    }

    @Composable
    fun MyUI() {
        AnimatedVisibility(visible = myPrefs.showText) {
            Text("Some text")
        }
    }
}
  • 主视图模型.kt:*
class MainViewModel : ViewModel() {
    companion object {
        val myPrefs by mutableStateOf( AppPrefs() )
    }
}
  • 练习二.kt:*
class ActivityTwo : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyTheme {
                Surface {
                    MyUI2()
                }
            }
        }
    }

    @Composable
    fun MyUI2() {
        val showText = remember {
            mutableStateOf(MainViewModel.myPrefs.showText)
        }

        Switch(
            checked = showText.value,
            onCheckedChange = {
                showText.value = it
                MainViewModel.myPrefs.showText = it
            }
        )
    }
}
  • 应用程序首选项kt:*
class AppPrefs {
    var showText: Boolean = false
}
toe95027

toe950271#

因为mutableStateOf正在跟踪myPrefs的状态,而myPrefs的状态从未改变(引用本身从未改变),所以未发生重组。
您可以通过几种方式实现重组。
在这里,我假设您的AppPrefs类已经包含了多个成员/字段,或者您希望在将来轻松地向其添加更多成员。这就是为什么我在下面的示例中向其添加了2个属性,并且我只建议添加更多成员不会影响现有代码的解决方案。

选项1

如果可以更改AppPrefs类以分别跟踪每个属性的状态,则进行以下更改

class MainViewModel : ViewModel() {
    companion object {
        val myPrefs = AppPrefs()
    }
}

class AppPrefs {
    var showText by mutableStateOf(false)
    var prop2 by mutableStateOf(0)
    var prop3: String? by mutableStateOf(null)
}

其他的一切都保持不变。这里重新组合再次工作,因为现在状态是在实际被更改的成员上跟踪的。

选项2

如果你不能(或者不想)在AppPrefs中包含mutableStateOf,你可以将AppPrefs从普通的class修改为data class,这样你就可以自动实现copy函数(与equalshashCodetoStringcomponentN一起实现反结构化)。

class MainViewModel : ViewModel() {
    companion object {
        // the only change here is val -> var
        var myPrefs by mutableStateOf(AppPrefs())
    }
}

// data class instead of a normal class
data class AppPrefs(
    val showText: Boolean = false,
    val prop2: Int = 0,
    val prop3: String? = null,
)

在这种情况下,还必须更改myPrefs值的更改方式。

onCheckedChange = {
        showText.value = it
        MainViewModel.myPrefs = MainViewModel.myPrefs.copy(showText = it)
    }

如果您不能使用数据类,那么您仍然可以使用选项2,但是您要在现有的类上实现copy函数

// normal class with a copy function
class AppPrefs(
    val showText: Boolean = false,
    val prop2: Int = 0,
    val prop3: String? = null,
) {
    fun copy(
        showText: Boolean = this.showText,
        prop2: Int = this.prop2,
        prop3: String? = this.prop3,
    ) = AppPrefs(showText, prop2, prop3)
}

作为奖励,如果您使用上述任何一种解决方案,您甚至可以简化MyUI2可组合代码的现有代码,并且重新组合仍然有效。
示例:

@Composable
fun MyUI2() {
    val showText = MainViewModel.myPrefs.showText

    Switch(
        checked = showText,
        onCheckedChange = {
            // for Option 1
            MainViewModel.myPrefs.showText = it
            // for Option 2
            MainViewModel.myPrefs = MainViewModel.myPrefs.copy(showText = it)
        }
    )
}

相关问题