Ui 从数据库中使用 Room 读取值时闪烁。

huangapple go评论85阅读模式
英文:

Ui flickering when reading value from database using room

问题

ViewModel

  1. class FlashcardViewModel(private val flashcardsRepository: FlashcardsRepository): ViewModel() {
  2. val timeoutMillis = 5_000L
  3. fun getFlashcardsFilteredByTopic(topic: String): StateFlow<List<Flashcard>> {
  4. val flashcardUiState: StateFlow<List<Flashcard>> = flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
  5. .stateIn(
  6. scope = viewModelScope,
  7. started = SharingStarted.WhileSubscribed(timeoutMillis),
  8. initialValue = listOf(Flashcard())
  9. )
  10. return flashcardUiState
  11. }
  12. }

用于显示闪卡的可组合函数

  1. @Composable
  2. fun FlashcardScreen(
  3. topic: String
  4. ) {
  5. val viewModel: FlashcardViewModel = viewModel(factory = Factory)
  6. val flashcardUiState by viewModel.getFlashcardsFilteredByTopic(topic).collectAsState()
  7. var currentIndex by rememberSaveable{ mutableStateOf(0) }
  8. val currentFlashcard = flashcardUiState[currentIndex]
  9. Flashcard(flashcard = currentFlashcard)
  10. }
  11. @Composable
  12. fun Flashcard(flashcard: Flashcard) {
  13. Text(
  14. text = flashcard.question
  15. )
  16. Text(
  17. text = flashcard.answer,
  18. )
  19. }
英文:

My code is supposed to use room to get flashcards filtered by topic and then display it. The flashcard does get displayed but the text keeps on flickering. Any help would be appreciated.

ViewModel

  1. class FlashcardViewModel(private val flashcardsRepository: FlashcardsRepository): ViewModel() {
  2. val timeoutMillis = 5_000L
  3. fun getFlashcardsFilteredByTopic(topic: String): StateFlow&lt;List&lt;Flashcard&gt;&gt; {
  4. val flashcardUiState: StateFlow&lt;List&lt;Flashcard&gt;&gt; = flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
  5. .stateIn(
  6. scope = viewModelScope,
  7. started = SharingStarted.WhileSubscribed(timeoutMillis),
  8. initialValue = listOf(Flashcard())
  9. )
  10. return flashcardUiState
  11. }
  12. }

Composable functions to display the flashcard

  1. @Composable
  2. fun FlashcardScreen(
  3. topic: String
  4. ) {
  5. val viewModel: FlashcardViewModel = viewModel(factory = Factory)
  6. val flashcardUiState by viewModel.getFlashcardsFilteredByTopic(topic).collectAsState()
  7. var currentIndex by rememberSaveable{ mutableStateOf(0) }
  8. val currentFlashcard = flashcardUiState[currentIndex]
  9. Flashcard(flashcard = currentFlashcard)
  10. }
  11. @Composable
  12. fun Flashcard(flashcard: Flashcard) {
  13. Text(
  14. text = flashcard.question
  15. )
  16. Text(
  17. text = flashcard.answer,
  18. )
  19. }

答案1

得分: 2

你在每次重新组合时都在调用 getFlashcardsFilteredByTopic。该函数会创建一个新的 StateFlow,其默认值是一个空卡片(listOf(Flashcard())),并且可能会执行数据库查询并使用结果更新流,从而触发重新组合并重新开始整个过程。

一个可能的解决方案是将 topic 放在 ViewModel 中,然后像这样做:

  1. // FlashcardViewModel
  2. val topic = MutableStateFlow("")
  3. val flashCards = topic.flatMapLatest { topic ->
  4. flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
  5. }.stateIn(...)

或者你可以在 ViewModel 中使用 remember 来记住你创建的流,这样只有当主题发生变化时才会调用 getFlashcardsFilteredByTopic

  1. // FlashcardScreen
  2. val flashCardsFlow = remember(topic) {
  3. viewModel.getFlashcardsFilteredByTopic(topic)
  4. }
  5. val flashcardUiState by flashCardsFlow.collectAsState()
英文:

You are calling getFlashcardsFilteredByTopic in each recomposition. This function creates new StateFlow with default value of empty card (listOf(Flashcard())) and presumably executes the database query and updates the flow with the result, which triggers the recomposition and it all starts again.

One possible solution would be to put the topic in the ViewModel and do something like this:

  1. // FlashcardViewModel
  2. val topic = MutableStateFlow(&quot;&quot;)
  3. val flashCards = topic.flatMapLatest { topic -&gt;
  4. flashcardsRepository.getFlashcardsStreamFilteredByTopic(topic = topic)
  5. }.stateIn(...)

Or you can remember the flow you create in ViewModel, this will only call getFlashcardsFilteredByTopic when the topic changes:

  1. // FlashcardScreen
  2. val flashCardsFlow = remember(topic) {
  3. viewModel.getFlashcardsFilteredByTopic(topic)
  4. }
  5. val flashcardUiState by flashCardsFlow.collectAsState()

huangapple
  • 本文由 发表于 2023年6月19日 03:18:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502180.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定