简体   繁体   中英

Android [Kotlin] - Inserting a new item into a selected Child-List on a MasterDetail-Flow using shared viewModel

I am quite new to android and i am trying to follow the recommended app architecture.

The app basically consists of two entities using a Room-Database: Training and Exercises. They have a many-to-many relationship, realized as proposed in the android developer guide with an extra reference entity and a 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>?

A training doesnt need exercises by default.

Now i want to list all trainings in a first Master-View (recyclerview) and if clicked on a training, i want to show the corresponding exercises in a separate Detail/Exercise-Recyclerview. I did this by two different fragments, and i am sharing the data via a ViewModel as proposed in the developer guide:

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

So, when i click on a training, i save the selection in the viewModel, tried to set an observer in the fragment

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

and display the selected training with the exercise list in the detail-fragment. In the detail fragment i also want to be able to insert new exercises.

Here comes my problem:
When i am adding a new exercise, i use the "insertExerciseToTraining" function above and then in the repository, i am inserting the exercise and a new trainingExerciseCrossRef. But in the detail fragment, the exercise list doesnt get updated. Only when i move back to the training overview and select the training again. Is this even the proper way of doing this, using a shared view model?
As a fast fix, i only share the training id in the viewModel and query the trainingWithExercise from the DB again, observing and displaying it. Then, on inserting new exercises, the list gets updated correctly. But that doesnt seem to be the best way achieving my goal or the best way using viewModels.
Any suggestions?

I came up with a workaround, but i am not sure if this is best practice. Maybe there is a cleaner way; but its working.

So basically, as above, i am selecting the training, and saving the id of the selection into the viewModel. Then i start the details-fragment, and observing the full allTrainingWithExercise-List/LiveData, then filtering this list for the training with the previous saved id and setting it in the view.
Therefore when i update the exercise list of the training by inserting a new exercise, the observed allTrainingsWithExercise gets updated, and i am "reselecting" the training by the saved training id.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM