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

使用芯片的习惯片段显示了两种不同的习惯类型"积极的"和"消极的",所以当用户判断积极的芯片时,回收器查看得到积极的习惯和使用负面芯片的相同的东西.

Here's How this operation work.

This is my DAO Code

@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)

And In my Repository, I map from "HabitEntity" to "HabitModel". So the function should return Flow<List< Habit>>.

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)
}

一百零二

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

Ok, after that I collect the latest changes in ViewModel and observe them in RecyclerView. Here's my ViewModel function.

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)
}

And Here's my 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)
        }
    }
}

Chips XML code In Habits Fragment

<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" />

And here's the code of the view changes when clicking on CheckBox completed in the habit using databiding.

    @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
    }

我的问题是

当我 Select 第一个芯片来显示数据并将其标记为完成或未完成时,它工作正常,数据更新没有任何问题.请看这个视频,充分了解https://youtube.com/shorts/bdRd70Me5nk?feature=share

如果我想从第一个芯片转移到另一个芯片,在不完成任何习惯的情况下获得不同的习惯类型,它也非常有效.https://youtube.com/shorts/t0Ma0BAE_Tw?feature=share

What If I want to mark a habit as completed and want to move from chip good habits to chip bad habits? And also if I have completed habits and want to move to another chip. Here's the problem in these two Situations. The RecycleView performs a very strange beehive process.https://www.youtube.com/shorts/6juhhWxq6_Y

I have tried to search for this problem for 4 days, but I didn't find any solutions that are useful or any that give me a full understanding of my problem.

注:

  • 我try 在Dao中使用"@UPDATE"查询来更新实体,同样也出现了同样的问题.
  • 我也try 过将getAllHabitsByType函数从(Dao中的Flow to Suspend)改为,当将一个习惯从NotComplete更改为Complete并移动到另一个芯片时,它运行得很好,但当然,当更新习惯状态以更新回收视图时,我需要在ViewModel中再次调用getAllHabits Fun,以及从另一个片段添加新习惯时的问题,我需要流在添加新习惯的时刻更新到我的回收视图**

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

Thank you.

推荐答案

我的应用程序的用户界面基本上是一样的,所以肯定有一种方法.

我认为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相关问答推荐

带有kSP而不是kapt的Hilt

Jetapck Compose:将Compose BOM更新为最新版本&2024.01.00&Quot;CircularProgressIndicator后出现错误

Android手柄注射周期错误,多个模块引用一个核心模块

Hilt+Worker NoSuchMethodException:<;init>;[class android.content.Context,class androidx.work.WorkerParameters]

未解析的引用:视图模型

如何将DrawableId参数传递给XML布局?

需要在按钮 onclick 上从 string.xml 获取值. @Composable 调用只能在@Composable 函数的上下文中发生

Jetpack compose (Glance) 小部件在加载位图图像后不会重新组合

在 Jetpack Compose 中自动滚动后面的项目

如何在 Jetpack Compose 中设置行宽等于 TextField 的宽度?

Jetpack Compose UI - 在 AlertDialog 中单击时按钮宽度会发生变化

组成不重叠的元素

如何使用 Jetpack Compose 在图像上叠加文本

没有互联网连接时,Firebase Storage putFile() 永远不会完成

您如何衡量条形图的 4 个类别?

如何在最后一个可见项目之后计算惰性列中的空白空间

我应该使用 Bluetooth Classic 还是 Bluetooth LE 与我的移动应用程序通信?

基线配置文件 x R8/Proguard

在 android-billing-5.0 中获取 ProductDetails 价格

无法再查看 Jetpack Compose 预览.无法实例化一个或多个类 (ComposeViewAdapter)