我有一个使用 jetpack compose 构建的电影应用程序。我在其中展示了用户选择的最喜欢的电影。收藏夹应该完美地存在于用户界面中,但是当我从用户界面中删除项目时,用户界面不会立即重新组合以反映更改。该怎么办?
这是我的用户界面
@composable fun favscreen( movieviewmodel: movieviewmodel ) { remember { mutablestateof(movieviewmodel.getfavmovies()) } val shouldshowdialog = remember { mutablestateof(false) } val uistates = movieviewmodel.favs.collectasstate() column { row { text( text = "fav movies", fontsize = 25.sp, fontfamily = fontfamily(font(r.font.nsb)), modifier = modifier.padding(20.dp).weight(1f) ) iconbutton(onclick = { shouldshowdialog.value = true }) { icon(icons.filled.delete, contentdescription = "") } } when(val currentstate = uistates.value){ is movieviewmodel.uistates.loading -> { box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center) { circularprogressindicator() } } is movieviewmodel.uistates.favs -> { val data = currentstate.data if(data.isempty()){ box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center) { text( text = "no fav movies", fontsize = 25.sp, fontfamily = fontfamily(font(r.font.nsb)), modifier = modifier.padding(20.dp)) } } else { lazycolumn(){ items(data){ fav -> row(modifier = modifier .padding(20.dp) .fillmaxwidth() .clip(roundedcornershape(16.dp)) .background(color = color.darkgray)) { asyncimage( model = utils.image_url + fav.imageurl, contentdescription = "", contentscale = contentscale.crop, filterquality = filterquality.high, modifier = modifier .width(150.dp) .height(150.dp)) column(modifier = modifier.padding(20.dp)) { text( text = fav.title!!, fontsize = 20.sp, fontfamily = fontfamily(font(r.font.nsb))) text( text = fav.rating.tostring() + "/10 imdb", fontsize = 15.sp, fontfamily = fontfamily(font(r.font.nsb))) text( text = "movie id : #" + fav.movieid, fontsize = 15.sp, fontfamily = fontfamily(font(r.font.nsb))) } } } } } } is movieviewmodel.uistates.error -> { box(modifier = modifier.fillmaxsize(), contentalignment = alignment.center){ text( text = "no fav movies", fontsize = 25.sp, fontfamily = fontfamily(font(r.font.nsb)), modifier = modifier.padding(20.dp)) } } else -> {} } } if (shouldshowdialog.value){ alertdialog( ondismissrequest = { shouldshowdialog.value = false }, confirmbutton = { textbutton(onclick = { movieviewmodel.deletemovies() shouldshowdialog.value = false }) { text(text = "proceed", fontfamily = fontfamily(font(r.font.nsm))) } }, dismissbutton = { textbutton(onclick = { shouldshowdialog.value = false }) { text(text = "cancel", fontfamily = fontfamily(font(r.font.nsm))) } }, shape = roundedcornershape(16.dp), text = { text(text = "favs deletion",fontfamily = fontfamily(font(r.font.nsb)))}, title = { text(text = "do you want to delete all fav movies ?", fontfamily = fontfamily(font(r.font.nsb)))} ) } }
这是我的视图模型
@HiltViewModel class MovieViewModel @Inject constructor( private val movieRepoImpl: MovieRepoImpl ) : ViewModel() { private val _favs : MutableStateFlow<UiStates> = MutableStateFlow(UiStates.INITIAL) val favs get() = _favs.asStateFlow() // MOVIE DAO fun getFavMovies() = viewModelScope.launch { try { _favs.value = UiStates.LOADING movieRepoImpl.getFavs().collectLatest { _favs.value = UiStates.FAVS(it) } } catch (ex : Exception){ _favs.value = UiStates.ERROR(ex.localizedMessage!!) } } fun insertMovie(favModel: FavModel) = viewModelScope.launch { movieRepoImpl.insertFav(favModel) } fun deleteMovies() = viewModelScope.launch { movieRepoImpl.deleteAllMovies() } sealed class UiStates { object LOADING : UiStates() data class FAVS(val data : MutableList<FavModel>) : UiStates() data class ERROR(val error : String) : UiStates() object INITIAL : UiStates() } }
当您查看 _favs
的写入用法时,问题变得非常明显:
只有 getfavmovies
会更新它。
虽然这听起来像“你应该在删除功能中更新你的用户界面状态”,但我想提出一个替代解决方案:
利用 room
+ flows
的力量!
首先在 viewmodel 中定义 loadingstate
,它将存储为流:
sealed class loadingstate { object loading : loadingstate() data class success : loadingstate() data class error(val error : string) : loadingstate() object initial : loadingstate() } private val loadingstate = mutablestateflow<loadingstate>(initial)
然后,更改 getfavmovies()
函数以使用此机制:
fun getfavmovies() { viewmodelscope.launch { try { _loadingstate.value = loadingstate.loading movierepoimpl.loadfavs() // a function which triggers e.g. a network call to actually load your movies _loadingstate.value = loadingstate.success } catch (ex: exception) { _loadingstate.value = loadingstate.error(ex.localizedmessage!!) } } }
最后,结合你的状态和数据,让奇迹发生。一旦其中之一发生变化,这将自动更新:)
val favs: flow<uistates> = combine( movierepoimpl.getmovies(), loadingstate ) { movies, loadingstate -> when (loadingstate) { is loadingstate.loading -> uistates.loading is loadingstate.success -> uistates.success(movies) is loadingstate.error -> uistates.error(error) is loadingstate. initial -> uistates.initial } }
免责声明:其中可能有拼写错误,我没有方便的 ide
让我们通过改进代码来结束这个答案:
避免将 =
用于不返回任何内容的函数(=
用于不返回任何内容的函数(getfavmovies
、insertmovie
、deletemovies
)。否则,你会例如期望 getfavmovies
、insertmovie
、deletemovies
)。否则,你会例如期望
remember { mutablestateof(movieviewmodel.getfavmovies()) }
并没有真正的 remember
任何东西,是吗?这太令人恼火了。我建议将其移至 viewmodel 中: init { movieviewmodel.getfavmovies() }
val uistates = movieviewmodel.favs.collectasstate()
:命名意味着这是多个 ui 状态,请使用单数; uistates
类本身也是如此。另外,如果这里直接使用“.value”,可以跳过这里的重新声明:when(val currentstate = uistates.value)
mutablelist9fed9bb6bc0c66d7a13f33ab448de4c5
。对 ui 状态使用可变值是一个坏主意,例如它可能会混淆您的 stateflow
您的 uistate 包含 。一定要避免这种情况。只需使用普通的旧列表:) https://www.php.cn/link/ff685590317f1330efc73f396ac92cd7
collectasstatewithlifecycle()
而不是 collectasstate()
使用 。请参阅https://medium.com/androiddevelopers/consuming-flows -安全地在jetpack-compose-cde014d0d5a3
repoimpl
您绝对不应该在 viewmodel 中访问
定义一个 screen-composable 是个好主意,它不需要 viewmodel,只需要数据,类似于 https://developer.android.com/jetpack/compose/state#state-hoisting
:.🎜
@Composable private fun FavScreen( val viewModel: MovieViewModel, ) { FawScreen( uiState = viewModel.fav.collectAsStateWithLifecycle().value, shouldShowDialog = viewModel.shouldShowDialog.collectAsStateWithLifecycle().value ) } @Composable private fun FavScreen( val uiState: UiStates, val shouldShowDialog: Boolean ) { // your current ui }
以上是使用房间数据库删除 jetpack 中的所有项目后,UI 未重新组合的详细内容。更多信息请关注PHP中文网其他相关文章!