android - 按 isCompleted 更新房间中的实体并使用流问题获取所有数据

标签 android kotlin android-room kotlin-coroutines kotlin-flow

我有一个习惯应用程序,用户可以添加不同类型的新习惯并将习惯标记为已完成/未完成。

Habits fragment 使用筹码显示具有两种不同习惯类型“积极”和“消极”的习惯,因此当用户检查积极筹码时,recyclerView 会获得积极习惯和消极筹码相同的内容。

这是此操作的工作原理。

这是我的 DAO 代码

@Query("SELECT * FROM HABIT_TABLE WHERE type = :type ORDER BY isCompleted ASC")
fun getAllHabitsByType(type: String): Flow<List<HabitEntity>>

@Query("UPDATE HABIT_TABLE SET isCompleted = :isCompleted WHERE habitId = :habitId")
suspend fun updateHabitByCompleted(habitId: Long, isCompleted: Boolean)

在我的存储库中,我将“HabitEntity”映射到“HabitModel”。所以函数应该返回 Flow>.

override fun getAllHabitsByType(type: HabitType): Flow<List<Habit>> {
    return channelFlow {
        dao.getAllHabitsByType(type.pathName).collect { habits ->
            send(habitMapper.map(habits))
        }
    }
}

override suspend fun updateHabitByCompleted(habit: Habit, isCompleted: Boolean) {
    dao.updateHabitByCompleted(habit.id, isCompleted)
}

我试图将从 dao 返回的流映射到 repo 函数,然后将其发送到 ViewModel 但没有成功,它应该收集数据然后将其发送到ViewModel 就像上面的函数一样。 这是我之前做的。

override fun getAllHabitsByType(type: HabitType): Flow<List<Habit>> {
    return flow { 
        dao.getAllHabitsByType(type.pathName).map { 
            emit(habitMapper.map(it))
        }
    }
}

好的,之后我收集 ViewModel 中的最新变化并在 RecyclerView 中观察它们。这是我的 ViewModel 函数。

private val _habitsList = MutableLiveData<List<Habit>>()
val habitsList: LiveData<List<Habit>> get() = _habitsList

private var currentHabitType = HabitType.POSITIVE

private fun getHabitsByType(habitType: HabitType) {
    viewModelScope.launch {
        repository.getAllHabitsByType(habitType).collectLatest {
            _habitsList.postValue(it)
        }
    }
}

override fun updateHabitByCompleted(habit: Habit, isCompleted: Boolean) {
    viewModelScope.launch {
        repository.updateHabitByCompleted(habit, isCompleted)
        getHabitsByType(currentHabitType)
    }
}

fun onChipTypeClick(habitType: HabitType) {
    currentHabitType = habitType
    getHabitsByType(habitType)
}

这是我的 HabitsFragment。

lateinit var habitsAdapter: HabitsAdapter

private fun initRecyclerVIew() {
    habitsAdapter = HabitsAdapter(emptyList(), viewModel)
    binding.habitsRecyclerView.adapter = habitsAdapter
}

private fun observeEvents() {
    viewModel.apply {
        ....
        habitsList.observe(viewLifecycleOwner) {
            habitsAdapter.setItems(it)
        }
    }
}

在习惯 fragment 中嵌入 XML 代码

<data>

    <variable
        name="viewModel"
        type="com.moataz.mohareb.presentation.habits.viewmodel.HabitsViewModel" />

    <variable
        name="habitType"
        type="com.moataz.mohareb.core.enums.HabitType" />
</data>

<com.google.android.material.chip.ChipGroup
    style="@style/Widget.Material3.Chip.Suggestion"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:selectionRequired="true"
    app:singleSelection="true">

     <com.google.android.material.chip.Chip
         style="@style/ChipStyle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:checked="true"
         android:onClick="@{() -> viewModel.onChipTypeClick(habitTYpe.POSITIVE)}"
         android:text="@string/good_habit"
         app:chipStrokeColor="@android:color/transparent" />

     <com.google.android.material.chip.Chip
         style="@style/ChipStyle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:onClick="@{() -> viewModel.onChipTypeClick(habitTYpe.NEGATIVE)}"
         android:text="@string/bad_habit"
         app:chipStrokeColor="@android:color/transparent" />
</com.google.android.material.chip.ChipGroup>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/habits_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="6dp"
        android:orientation="vertical"
        android:overScrollMode="never"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/horizontal_chips_scroll_view"
        tools:listitem="@layout/item_habit" />

下面是使用databiding习惯完成的点击CheckBox时 View 变化的代码。

    @BindingAdapter(value = ["app:changeTextStatue"])
    fun changeTextStatue(textView: TextView, isCompleted: Boolean) {
        if (isCompleted) {
            textView.paintFlags = textView.paintFlags or android.graphics.Paint.STRIKE_THRU_TEXT_FLAG
        } else {
            textView.paintFlags =
                textView.paintFlags and android.graphics.Paint.STRIKE_THRU_TEXT_FLAG.inv()
        }
    }

   @BindingAdapter(value = ["app:changeCheckBoxStatue"])
   fun changeCheckBoxStatue(checkBox: CheckBox, isCompleted: Boolean) {
        checkBox.isChecked = isCompleted == true
    }

我遇到的问题

当我选择第一个芯片显示数据并标记为完成或未完成时,它工作正常,数据更新没有任何问题。请观看此视频以全面了解 https://youtube.com/shorts/bdRd70Me5nk?feature=share

如果我想从第一个芯片移动到另一个芯片以获得不同的习惯类型而不完成任何习惯,它也很有效。 https://youtube.com/shorts/t0Ma0BAE_Tw?feature=share

如果我想将一个习惯标记为已完成并想从重要的好习惯转变为重要的坏习惯怎么办?而且如果我已经养成习惯并想转移到另一个芯片。这是这两种情况下的问题。 RecycleView 执行一个非常奇怪的 hive 过程。 https://www.youtube.com/shorts/6juhhWxq6_Y

我已尝试搜索此问题 4 天,但我没有找到任何有用的解决方案或任何让我完全了解我的问题的解决方案。

注意:

  • 我尝试在 dao 中使用“@UPDATE”查询来更新实体,但也出现了同样的问题。
  • 我也尝试过将 getAllHabitsByType 函数从(在 dao 中暂停)更改为 getAllHabitsByType 函数,并且在将习惯从 notCompleted 更改为 completed 并移动到另一个芯片时它运行良好,没有任何问题,但是当然,我需要调用当更新习惯状态以更新 recyclerview 时,ViewModel 中的 getAllHabits 再次变得有趣,以及从另一个 fragment 添加新习惯时出现的问题,我需要在添加新习惯时流程来更新我的 recyclerview **

这是我在 github 上的项目源代码,以全面了解我的代码 https://github.com/MoatazBadawy/Mohareb

谢谢。

最佳答案

我的应用程序中的 UI 类型基本相同,所以肯定有办法。

我认为 channelFlow 是罪魁祸首,但也有很多回调使确定列表中项目的确切垃圾邮件设置变得复杂。

override fun getAllHabitsByType(type: HabitType): Flow<List<Habit>> {
    return dao.getAllHabitsByType(type.pathName).map { habits ->
        habitMapper.map(habits)
    }
}

这绝对有效。

LiveData/Flow 的主要优点是您无需手动更新它,它会自动更新。有/可能不需要调用 getHabitsByType。您只需要将这些输入也设为 Flow/LiveData

private val currentHabitType = MutableStateFlow(HabitType.POSITIVE)

private val _habitsList = currentHabitType.flatMapLatest { type ->
    repository.getAllHabitsByType(type)
}

val habitsList: LiveData<List<Habit>> get() = _habitsList.asLiveData()

这应该是您需要的全部内容,以涵盖来自数据库的自动更新以及通过芯片进行的任何更改。

关于android - 按 isCompleted 更新房间中的实体并使用流问题获取所有数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74141631/

相关文章:

java - 当第一个列表项不可达时,GC 是否会收集链表?

android.support.test.espresso.NoActivityResumedException : No activities in stage RESUMED

android - 如何在 Android Jetpack Compose 的可组合物中启动协程

Kotlin 声明冲突

android - SQL 删除 NOT IN 不起作用的地方

Android 2.2 蓝牙问题(采用 BluetoothChat 示例)

android - 如何找出布局在项目中使用的位置?

android - Activity 之外的 Firebase 数据库 EventListener

android - 在 Android 上使用 Room 时如何安装最新版本的 Sqlite aar

android - 通过 AsyncTask 在 onCreate() 期间加载数据