【问题标题】:Correct implementation of MVVMMVVM的正确实现
【发布时间】:2023-03-29 13:37:01
【问题描述】:

请你回答我关于 MVVM 的一些问题

我目前正在制作一个锻炼日记应用程序,其中主要片段在列表中显示所有最新创建的练习 (RecyclerView) 还有一个片段是我创建这些练习的地方和一个日历片段,它在日历上用红点标记当天是否有锻炼

我想在日历下显示当我点击某个日期时我当天所做的练习列表。

现在是如何实现的:

这是我的日历课。我将 Materialcalendarview 库用于日历

class CalendarFragment : Fragment(), OnDateSelectedListener, OnMonthChangedListener {

private lateinit var mViewModel: CalendarVIewModel
private lateinit var binding: CalendarBinding
private val dates = ArrayList<CalendarDay>()
private var mExercises = ArrayList<Exercise>()
private val adapter = CalendarAdapter()


override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    binding = CalendarBinding.inflate(inflater, container, false)
    binding.calendarView.setOnDateChangedListener(this)
    setupViewModel()
    observerLiveData()
    setupRV()
    return binding.root
}

override fun onDateSelected(
    widget: MaterialCalendarView,
    date: CalendarDay,
    selected: Boolean
) {
    adapter.setList(getExerciseListOnThisData(date))
}


private fun setTrainingsInCalendar(exercise: List<Exercise>) {
    val cal = Calendar.getInstance()
    for (i in exercise.indices) {
        cal.time = convertLongToTime(exercise[i].timestamp)
        val month = cal[Calendar.MONTH]
        val day = cal[Calendar.DAY_OF_MONTH]
        val year = cal[Calendar.YEAR]
        val temp: LocalDate = LocalDate.of(year, month + 1, day)
        val dayd = CalendarDay.from(temp)
       dates.add(dayd)
        mExercises.add(exercise[i])
    }
    binding.calendarView.addDecorator(EventDecorator(Color.RED, dates))
}

private fun setupRV(){
    binding.apply {
        calendarRecyclerView.layoutManager = LinearLayoutManager(context)
        calendarRecyclerView.adapter = adapter
    }
}


private fun setupViewModel() {
    mViewModel =
        ViewModelProvider(this).get(CalendarVIewModel::class.java)
}

private fun observerLiveData() {
    mViewModel.exercises.observe(viewLifecycleOwner){
        setTrainingsInCalendar(it)
    }
}

override fun onMonthChanged(widget: MaterialCalendarView?, date: CalendarDay?) {
}


private fun getExerciseListOnThisData(date: CalendarDay): List<Exercise>{
    val mExercisesList = ArrayList<Exercise>()
    for (i in mExercises.indices) {
        if(calendarDay(mExercises[i].timestamp) == date){
            mExercisesList.add(mExercises[i])
        }
    }
    return mExercisesList
}

在有趣的 onDateSelected 中,我必须将这一天的练习列表传递给适配器。 第一个问题是:如何使用 MVVM 模式正确传递列表? 现在,当导航到片段时,我在片段的 ViewModel 中订阅了我的 LiveDate 。 LiveDate 我从一个房间的请求中得到

class CalendarVIewModel@ViewModelInject constructor(private val exercisesRepository: ExercisesRepository)
: ViewModel() {
val exercises: LiveData<List<Exercise>> =
    exercisesRepository.allExercises

而且当我订阅 LiveData 时,我还会在有趣的 setTrainingsInCalendar 中膨胀我的

private var mExercises = ArrayList<Exercise>()

第二个问题是,这样做有多正确?也许在 ViewModel 中实现所有这些会更好? 还有第三个问题:在已经准备好的 List 的 Room DAO 中提出请求有多正确。例如,传递一个现成的练习列表,而不是订阅 LiveData,例如,像这样:

@Query("SELECT * FROM Exercises)
fun getAllExercises(): List<Exercise>>

setTrainingsInCalendar(exercise: List<Exercise> get from DAO Room)

谢谢。我只是 MVVM 的初学者

【问题讨论】:

  • I ask you to answer some of my questions 我不认为这是获得 SO 答案的最佳方法,你最好一次问一个具体问题,这只是我的建议

标签: android kotlin


【解决方案1】:

免责声明:此答案基于我的观点。

让我们从最后一个问题开始。如果数据库中的表在用户在相关屏幕上停留的时间内没有发生变化,则无需在 DAO 类中返回 LiveData

@Query("SELECT * FROM Exercises)
fun getAllExercises(): List<Exercise>>

话虽如此,如果你确实直接返回List,你将不得不在后台线程中这样做,所以你需要使用某种观察者/事件模式,以便通知视图数据是可用的。所以你不妨坚持当前的实现。

现在,视图类(即Fragment)不应该处理逻辑。那应该是ViewModel 的责任。像这样:

class CalendarViewModel@ViewModelInject constructor(
    private val exercisesRepository: ExercisesRepository
): ViewModel() {
    private val exercises: LiveData<List<Exercise>> = exercisesRepository.allExercises

    val exerciseInstants = Transformations.map(exercises) { exercises ->
        exercises.map { Instant.parse(it.timestamp) }
    }

    fun getExercisesForDate(date: Instant): List<Exercise> {
        return requireNotNull(exercises.value).filter {
            Instant.parse(it.timestamp) == date
        }
    }
}

请注意,为了解释,我使用了Instant 而不是LocalDateTime/CalenderDay。此外,您不应该在ViewModel 中使用视图类(即CalendarDay),LocalDateTime 很好。有一天,您可能想为日历视图使用另一个库,这不会影响您的 ViewModel

现在在片段中,您可以订阅exerciseInstants 并设置日历的日期,您可以调用ViewModel 方法getExercisesForDate 来获取特定日期的练习。

override fun onDateSelected(
    widget: MaterialCalendarView,
    date: CalendarDay,
    selected: Boolean
) {
    adapter.setList(mViewModel.getExercisesForDate(...)))
}

private fun setBindings() {
    mViewModel.exerciseInstants.observe(viewLifecycleOwner) {
        setTrainingsInCalendar(...)
    }
}

不要在Fragment 中创建datesmExercises 等字段。使用来自ViewModel的数据

【讨论】:

  • 非常感谢您的建议,一切都按照您的示例进行。也得到了你对我关于 MVVM 的问题的回答
猜你喜欢
  • 1970-01-01
  • 2020-11-21
  • 2023-03-23
  • 1970-01-01
  • 2012-11-11
  • 2017-12-06
  • 2017-06-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多