首頁  >  文章  >  使用房間資料庫刪除 jetpack 中的所有項目後,UI 未重新組合

使用房間資料庫刪除 jetpack 中的所有項目後,UI 未重新組合

WBOY
WBOY轉載
2024-02-06 08:15:04537瀏覽
問題內容

我有一個使用 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

讓我們透過改進程式碼來結束這個答案:

  1. 避免將 = 用於不傳回任何內容的函數(getfavmoviesinsertmoviedeletemovies)。否則,你會例如期望 getfavmovies 返回電影列表

  2. remember { mutablestateof(movieviewmodel.getfavmovies()) } 並沒有真正的 remember 任何東西,是嗎?這太惱人了。我建議將其移至 viewmodel 中: init { movieviewmodel.getfavmovies() }

  3. val uistates = movieviewmodel.favs.collectasstate():命名表示這是多個 ui 狀態,請使用單數; uistates 類別本身也是如此。另外,如果這裡直接使用“.value”,可以跳過這裡的重新宣告:when(val currentstate = uistates.value)

  4. 您的 uistate 包含 mutablelist9fed9bb6bc0c66d7a13f33ab448de4c5。對 ui 狀態使用可變值是一個壞主意,例如它可能會混淆您的 stateflow。一定要避免這種情況。只需使用普通的舊列表:) https://www.php.cn/link/ff685590317f1330efc73f396ac92cd7

  5. 使用 collectasstatewithlifecycle() 而不是 collectasstate()。請參閱https://medium.com/androiddevelopers/consuming-flows -安全地在jetpack-compose-cde014d0d5a3

  6. 您絕對不應該在 viewmodel 中存取 repoimpl:將儲存庫拆分為介面和實作的整個想法是隱藏實作

  7. 定義一個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中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除