首页 > 解决方案 > Android [Kotlin] - 使用共享视图模型将新项目插入到 MasterDetail-Flow 上的选定子列表中

问题描述

我对 android 很陌生,我正在尝试遵循推荐的应用程序架构。

该应用程序基本上由两个使用房间数据库的实体组成:培训和练习。它们具有多对多关系,按照 android 开发人员指南中的建议实现,带有一个额外的参考实体和一个 POJO:

@Entity(
    tableName = "training_exercise_ref",
    primaryKeys = ["trainingId", "exerciseId"])
data class TrainingExerciseCrossRef (
    var trainingId: Long,
    var exerciseId: Long
)

data class TrainingWithExercise (
    @Embedded val training: Training,

    @Nullable
    @Relation(
        parentColumn = "trainingId",
        entityColumn = "exerciseId",
        associateBy = Junction(TrainingExerciseCrossRef::class)
    )
    val exercises: List<Exercise>?

默认情况下,培训不需要练习。

现在我想在第一个 Master-View (recyclerview) 中列出所有培训,如果单击培训,我想在单独的 Detail/Exercise-Recyclerview 中显示相应的练习。我通过两个不同的片段来做到这一点,并且我通过开发人员指南中建议的 ViewModel 共享数据:

class DashboardViewModel(application: Application) : AndroidViewModel(application) {

    private val trainingRepository: TrainingRepository

    val allTrainingWithExercises: LiveData<List<TrainingWithExercise>>
    val selectedTraining = MutableLiveData<TrainingWithExercise>()

    init {
        val trainingDao = TrainingRoomDatabase.getDatabase(application, viewModelScope).trainingDao()
        trainingRepository = TrainingRepository(trainingDao)

        allTrainingWithExercises = trainingRepository.allTrainingWithExerciseEntries
    }

    fun selectTraining(trainingWithExercise: TrainingWithExercise) {
        selectedTraining.value = trainingWithExercise
    }

    fun insertExerciseToTraining(trainingId: Long, exercise: Exercise) = viewModelScope.launch {
        val newExerciseId: Long = exerciseRepository.insert(exercise)
        trainingRepository.insertExerciseOfTraining(trainingId, newExerciseId)
    }

因此,当我单击培训时,我将选择保存在 viewModel 中,尝试在片段中设置观察者

dashboardViewModel.selectedTraining.observe(viewLifecycleOwner, Observer { exercises ->
            exercises?.let { adapter.setTraining(it) }
        })

并在 detail-fragment 中显示所选训练和练习列表。在细节片段中,我还希望能够插入新的练习。

我的问题来了:
当我添加一个新练习时,我使用上面的“insertExerciseToTraining”函数,然后在存储库中,我插入练习和一个新的 trainingExerciseCrossRef。但是在细节片段中,练习列表没有得到更新。只有当我回到培训概览并再次选择培训时。这甚至是使用共享视图模型的正确方法吗?
作为快速修复,我只在 viewModel 中共享训练 id,并再次从数据库中查询 trainingWithExercise,观察并显示它。然后,在插入新练习时,列表会正确更新。但这似乎不是实现我的目标的最佳方式,也不是使用 viewModel 的最佳方式。
有什么建议么?

标签: androidkotlinviewmodelandroid-roommaster-detail

解决方案


我想出了一个解决方法,但我不确定这是否是最佳做法。也许有更清洁的方法;但它的工作。

所以基本上,如上所述,我正在选择训练,并将选择的 id 保存到 viewModel 中。然后我启动详细信息片段,并观察完整的 allTrainingWithExercise-List/LiveData,然后使用先前保存的 id 过滤此列表以进行训练并将其设置在视图中。
因此,当我通过插入新练习来更新训练的练习列表时,观察到的 allTrainingsWithExercise 会更新,并且我正在通过保存的训练 ID“重新选择”训练。

dashboardViewModel.allTrainingWithExercises.observe(viewLifecycleOwner, Observer { trainings ->
            trainings?.let { adapter.setTraining(it.filter { it.training.trainingId == dashboardViewModel.selectedTrainingId }[0]) }
        })

推荐阅读