- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
当我更改 ViewModel 变量时,可组合项不会更新 View ,我不确定该怎么做。
这是我的主要 Activity :
class MainActivity : ComponentActivity() {
companion object {
val TAG: String = MainActivity::class.java.simpleName
}
private val auth by lazy {
Firebase.auth
}
var isAuthorised: MutableState<Boolean> = mutableStateOf(FirebaseAuth.getInstance().currentUser != null)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val user = FirebaseAuth.getInstance().currentUser
setContent {
HeroTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
if (user != null) {
Menu(user)
} else {
AuthTools(auth, isAuthorised)
}
}
}
}
}
}
我有一个 View 模型:
class ProfileViewModel: ViewModel() {
val firestore = FirebaseFirestore.getInstance()
var profile: Profile? = null
val user = Firebase.auth.currentUser
init {
fetchProfile()
}
fun fetchProfile() {
GlobalScope.async {
getProfile()
}
}
suspend fun getProfile() {
user?.let {
val docRef = firestore.collection("Profiles")
.document(user.uid)
return suspendCoroutine { continuation ->
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
this.profile = getProfileFromDoc(document)
}
}
.addOnFailureListener { exception ->
continuation.resumeWithException(exception)
}
}
}
}
}
以及基于用户身份验证的可组合 View :
@Composable
fun Menu(user: FirebaseUser) {
val context = LocalContext.current
val ProfileVModel = ProfileViewModel()
Column(
modifier = Modifier
.background(color = Color.White)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("Signed in!");
ProfileVModel.profile?.let {
Text(it.username);
}
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxWidth()
) {
TextButton(onClick = {
FirebaseAuth.getInstance().signOut()
context.startActivity(Intent(context, MainActivity::class.java))
}) {
Text(
color = Color.Black,
text = "Sign out?",
modifier = Modifier.padding(all = 8.dp)
)
}
}
}
}
当我的 Firestore 方法返回时,我更新了 profile
var,并“期望”它在可组合项中更新,在这里:
ProfileVModel.profile?.let {
Text(it.username);
}
然而,什么都没有改变?
当我从可组合内部添加 firebase 函数时,我可以这样做:
context.startActivity(Intent(context, MainActivity::class.java))
它会更新 View 。但是,我不太确定如何从 ViewModel 内部执行此操作,因为“上下文”是 Composable 特定的功能?
我已尝试查找实时数据,但每个教程要么太令人困惑,要么与我的代码不同。我来自 SwiftUI MVVM,所以当我更新 ViewModel 中的某些内容时,任何使用该值的 View 都会更新。这里似乎不是这种情况,我们将不胜感激。
谢谢。
最佳答案
ViewModel
正确在下面标记的行中,您将 View 模型设置为新的 ProfileViewModel
在你的 Menu
的每次重组上实例可组合,这意味着您的 View 模型(以及它跟踪的任何状态)将在每次重组时重置。这会阻止您的 View 模型充当 View 状态持有者。
@Composable
fun Menu(user: FirebaseUser) {
val context = LocalContext.current
val ProfileVModel = ProfileViewModel() // <-- view model resets on every recomposition
// ...
}
您可以通过始终获取您的 ViewModel
来解决此问题来自 ViewModelStore
.以这种方式ViewModel
将具有正确的所有者(正确的生命周期所有者),因此具有正确的生命周期。Compose 有一个帮助程序来获取 ViewModel
s 与 viewModel()
打电话。
这就是您在代码中使用调用的方式
@Composable
fun Menu(user: FirebaseUser) {
val context = LocalContext.current
val ProfileVModel: ProfileViewModel = viewModel()
// or this way, if you prefer
// val ProfileVModel = viewModel<ProfileViewModel>()
// ...
}
另见 ViewModels in Compose
概述了与 ViewModel
相关的基础知识s 在 Compose 中。
Note: if you are using a DI (dependency injection) library (such as Hilt, Koin...) then you would use the helpers provided by the DI library to obtain
ViewModel
s.
GlobalScope
(除非你确切地知道你为什么需要它)并注意异常如 Avoid Global Scope 中所述你应该避免使用 GlobalScope
只要有可能。安卓ViewModel
它们带有自己的协程范围,可通过 viewModelScope
访问.你还应该 watch out for exceptions .
代码示例
class ProfileViewModel: ViewModel() {
// ...
fun fetchProfile() {
// Use .launch instead of .async if you are not using
// the returned Deferred result anyway
viewModelScope.launch {
// handle exceptions
try {
getProfile()
} catch (error: Throwable) {
// TODO: Log the failed attempt and/or notify the user
}
}
}
// make it private, in most cases you want to expose
// non-suspending functions from VMs that then call other
// suspend factions inside the viewModelScope like fetchProfile does
private suspend fun getProfile() {
// ...
}
// ...
}
Best practices for coroutines in Android 中涵盖了更多协程最佳实践.
Compose 通过 State<T>
跟踪状态.如果你想管理状态,你可以创建 MutableState<T>
带有 mutableStateOf<T>(value: T)
的实例, 其中value
参数是您要用来初始化状态的值。
你可以像这样在你的 View 模型中保持状态
// This VM now depends on androidx.compose.runtime.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
class ProfileViewModel: ViewModel() {
var profile: Profile? by mutableStateOf(null)
private set
// ...
}
那么每次你都会改变profile
变量,以某种方式使用它(即读取它)的可组合项将进行重组。
但是,如果你不想要你的 View 模型ProfileViewModel
依赖于 Compose 运行时,那么还有其他选项可以在不依赖于 Compose 运行时的情况下跟踪状态更改。来自文档部分 Compose and other libraries
Compose comes with extensions for Android's most popular stream-basedsolutions. Each of these extensions is provided by a differentartifact:
Flow.collectAsState()
doesn't require extra dependencies. (because it is part ofkotlinx-coroutines-core
)
LiveData.observeAsState()
included in theandroidx.compose.runtime:runtime-livedata:$composeVersion
artifact.
Observable.subscribeAsState()
included in theandroidx.compose.runtime:runtime-rxjava2:$composeVersion
or>androidx.compose.runtime:runtime-rxjava3:$composeVersion
artifact.These artifacts register as a listener and represent the values as a
State
. Whenever a new value is emitted, Compose recomposes those partsof the UI where thatstate.value
is used.
这意味着您还可以使用 MutableStateFlow<T>
跟踪 ViewModel
内的变化并将其作为 StateFlow<T>
暴露在您的 View 模型之外.
// This VM does not depend on androidx.compose.runtime.* anymore
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
class ProfileViewModel : ViewModel() {
private val _profileFlow = MutableStateFlow<Profile?>(null)
val profileFlow = _profileFlow.asStateFlow()
private suspend fun getProfile() {
_profileFlow.value = getProfileFromDoc(document)
}
}
然后使用 StateFlow<T>.collectAsState()
在您的可组合项中获取 State<T>
这是 Compose 所需要的。
A general
Flow<T>
can also be collected asState<T>
withFlow<T : R>.collectAsState(initial: R)
, where the initial value has to be provided.
@Composable
fun Menu(user: FirebaseUser) {
val context = LocalContext.current
val ProfileVModel: ProfileViewModel = viewModel()
val profile by ProfileVModel.profileFlow.collectAsState()
Column(
// ...
) {
// ...
profile?.let {
Text(it.username);
}
// ...
}
}
要了解有关在 Compose 中使用状态的更多信息,请参阅 Managing State 上的文档部分.这是能够在 Compose 中使用状态并有效触发重组的基本信息。它还涵盖了 state hoisting 的基础知识.如果您更喜欢此处的编码教程,请访问 code lab for State in Jetpack Compose .
有关随着复杂性增加而处理状态的介绍,请参阅来自 Google 的关于 Using Jetpack Compose's automatic state observation 的视频。 .
关于android - 当我更改 ViewModel var 时,可组合项不会在 Kotlin + Compose 中更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73115009/
我使用以下命令使用 Composer 删除了一个包。 composer remove sjparkinson/static-review 以上命令从 composer.json 中删除条目文件但 co
我刚刚开始使用 Composer 功能,你告诉它查看本地目录的依赖关系,这样你就可以开发一个库和一些并行使用该库的东西,而不必一直推送到 git 来更新,这是惊人的。例如 "repositories"
composer 和有什么区别和 composer.phar ? 例子: composer install composer.phar install 我一直看到使用 composer.phar 编写
阅读docker-compose -h或this official manual的帮助将为我们提供--project-directory PATH选项 docker-compose [-f ...]
我已经使用他们的安装指南在我的 Linux/Apache 服务器上的根目录(这是默认选择)中成功安装了 Composer。到目前为止,一切都非常简单,除了我必须进行的一个 php.ini 调整( de
在我的 composer.json配置文件,我有: "require": { "zendframework/zend-log" : "~2.3", }, "require-dev": {
从 Composer 安装此软件包后,我想复制位于软件包内的一些文件。 实际上,我希望在从 Composer 安装或更新软件包后,将下载的软件包中可能存在的某个文件复制到另一个目录。我用 script
我对码头公司还是个新手。我使用的是最新版本的Python、Django和Docker。我已经在这个项目上工作了两周了,我已经创建了docker-compose.yml文件,并且已经构建了我的docke
我正在尝试使用 composer 安装一个 github 项目并得到以下错误 Composer [UnexpectedValueException]您的 github.com 的 Github oau
我开发 Symfony 包的工作流程如下: 安装 Symfony 为新包创建一个 git repo,在其中放置一个 composer.json 文件 需要顶级 composer.json 中的新包,使
我正在尝试使用 composer 安装 github 项目并收到以下错误 Composer [UnexpectedValueException] 您用于 github.com 的 Github oau
我们在项目中使用了 composer。当我开发和/或向项目提交任何内容时,我通常应该使用 composer install;更新依赖项只是偶尔进行。 我们还使用 https://github.com/
我在 youtube 上学会了这个抽屉 https://www.youtube.com/watch?v=JLICaBEiJS0&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9
我知道在 onClick 中调用可组合函数是不可能的。 @Composable 调用只能在 @Composable 函数的上下文中发生 撰写版本 - alpha06 但我坚持以下要求。 要求是, 在
这是我的 docker-compose.yml 文件: version: '3.1' services: a: image: tutum/hello-world b: imag
创建Asset实例时是否有auto_increment字段类型可用。例如, Assets ID 应该是自动生成的字段,应该在运行时创建,而不是在应用程序级别提及该值。我可以通过创建一个交易处理器函数来
在 Composer 项目中,我必须添加一个库,它没有 composer.json 并且不使用命名空间。因此,我调整了项目的 composer.json 以添加库: { [...] "
当 vendor 目录中已经有一些组件被下载时.. 在上面运行 install 以及运行 update 时有什么影响? 最佳答案 所以我有同样的问题,这是我发现的: composer install
尝试运行 composer install 时出现此错误。我已经运行了 composer update,我正在尝试使用这个最新的锁定文件进行安装。没有任何帮助。 Loading composer re
当我尝试做: $ sudo php composer.phar update 我收到此警告: Warning: This development build of composer is over 3
我是一名优秀的程序员,十分优秀!