我有两个活动:ActivityOne.ktActivityTwo.kt.它们都使用Jetpack Compose来显示UI.在第一个例子中,一个项目(比如Text)必须用变量showtText表示为true,当它为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(第一个在后台运行,不会从堆栈中清除).
  2. 这里有一个Switch可以切换showText的值.
  3. 当用户按"后退"时,ActivityTwo.kt调用finish(),它消失,再次显示ActivityOne.kt(它在堆栈中).
  4. 如果用户在ActivityTwo.kt中切换了showText值,则文本应自动隐藏或显示,因为showText的状态已更改.

问题是,尽管showText的值确实发生了变化,但ActivityOne.kt中的UI没有响应这些变化.我已经判断过ActivityOne.kt的UI在ActivityTwo.kt完成后没有重新编译,因为它不记得之前的状态showText.

我怎样才能做到呢?提前感谢!

EDIT 1

ActivityOne.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")
        }
    }
}

MainViewModel.kt:

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

ActivityTwo.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
            }
        )
    }
}

AppPrefs.kt:

class AppPrefs {
    var showText: Boolean = false
}

推荐答案

重新编译没有发生,因为mutableStateOf正在跟踪从未更改的myPrefs的状态(参考本身从未更改).

可以通过几种方式实现重新编译.

在这里,我假设您的AppPrefs类已经包含多个成员/字段,或者您希望在将来轻松地向其添加更多成员.这就是为什么我在下面的示例中又添加了2个属性,并且我只建议了添加更多成员不会影响现有代码的解决方案.

Option 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)
}

其他一切都保持不变.在这里,重新编译再次起作用,因为现在在实际更改的成员上跟踪状态.

Option 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)
        }
    )
}

Kotlin相关问答推荐

为什么在Spring中,对事务性方法调用的非事务方法调用仍然在事务中运行?

可以从背景图像中点击图标吗?

在构造函数中创建内部类实例时,只能使用包含类的接收器调用内部类的构造函数

我可以更改方法中泛型类的类型参数边界吗?

哪个更好? Integer.valueOf(readLine()) 或 readLine()!!.toInt()

可以在没有导入声明的情况下调用 Kotlin 扩展函数吗?

如何处理基于枚举提前返回的 forEach 循环,Kotlin 中的一条路径除外

Kotlin 列表扩展功能

你怎么知道什么时候需要 yield()?

T except one class

在 Kotlin 中,当枚举类实现接口时,如何解决继承的声明冲突?

Kotlin 语言是如何用 Kotlin 编写的?

Kotlin 顶级函数与对象函数

Hilt Activity 必须附加到 @AndroidEntryPoint 应用程序

在Kotlin中传递并使用函数作为构造函数参数

将ExpectedException与Kotlin一起使用

如何让数据类在Kotlin中实现接口/扩展超类属性?

保存对象时未填充 Spring Boot JPA@CreatedDate @LastModifiedDate

我应该使用Kotlin数据类作为JPA实体吗?

Kotlin,什么时候按map授权?