繁体   English   中英

嵌套 RecyclerView 中的 LiveData

[英]LiveData in Nested RecyclerView

我现在正在编码的片段应该为用户提供他的膳食计划时间表的日历概览。 因此,通过日期选择器,他可以选择一个时间段,程序将向用户显示他为所选工作日选择了哪些食谱。 因此,我构建了一个嵌套的 RecyclerView,其中工作日作为父层,相应的食谱作为子层。 工作日层的数据 class 如下所示:

data class Weekday (
    val weekday : String,
    val listWithRecipes : List<Recipe>?
        )

Recipe 实体的 class 如下所示:

@Entity(tableName = "Recipe")
@Parcelize
data class Recipe(
    @PrimaryKey var recipeName : String,
    var description : String?,
    var serving : Int,
    var preparationTime : Int?
) : Parcelable

顶部 Recycler View 的适配器如下所示:

class MealPlanAdapter(private var mealplan: List<Weekday>) :
    RecyclerView.Adapter<MealPlanAdapter.MealPlanViewHolder>(), RecipeAdapter.OnItemClickListener {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MealPlanViewHolder {
        return MealPlanViewHolder(
            DailyMealplanItemBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
        )
    }

    override fun getItemCount() = mealplan.size

    override fun onBindViewHolder(holder: MealPlanViewHolder, position: Int) {
        val weekday = mealplan[position]
        val recipeAdapter = RecipeAdapter(this)
        recipeAdapter.submitList(weekday?.listWithRecipes)
        holder.dayOfWeek.text = weekday.weekday
        val recipeLayoutManager = LinearLayoutManager(holder.recyclerView.context,RecyclerView.VERTICAL, false)
        recipeLayoutManager.initialPrefetchItemCount = 4
        holder.recyclerView.apply{
            layoutManager = recipeLayoutManager
            adapter = recipeAdapter
        }

    }

    fun setSchedule(mealplan : List <Weekday>){
        this.mealplan = mealplan
        notifyDataSetChanged()
    }

    inner class MealPlanViewHolder(val binding: DailyMealplanItemBinding) :
        RecyclerView.ViewHolder(binding.root) {
        val recyclerView: RecyclerView = binding.rvRecyclerView
        val dayOfWeek: TextView = binding.tvDayOfWeek
    }

    override fun onItemClick(recipe: Recipe) {
        TODO("Not yet implemented")
    }
}

每当用户更改时间段时,都会在片段中调用适配器中的 setScheduled() 方法。

 materialDatePickerStartDate.addOnPositiveButtonClickListener(
            MaterialPickerOnPositiveButtonClickListener<Any?> { selection ->
                _binding.viewmodel!!.startDateInUTCFormat = selection as Long
                _binding.tvStartDate.setText(materialDatePickerStartDate.headerText)
                adapter.setSchedule(
                    _binding.viewmodel!!.returnListWithWeekDaysAndCorrespondingRecipes(
                        _binding.viewmodel!!.startDateInUTCFormat,
                        _binding.viewmodel!!.endDateInUTCFormat
                    )


                )
            }

        )

视图模型如下所示:

@HiltViewModel
class MealplanViewModel @Inject constructor(
    val mealPlanRepository: MealPlanRepository
) : ViewModel() {
    private lateinit var _binding: FragmentMealPlanBinding
    var startDateInUTCFormat: Long = System.currentTimeMillis()
    var endDateInUTCFormat: Long = System.currentTimeMillis()    

    fun returnListWithWeekDaysAndCorrespondingRecipes(
        startDate: Long,
        endDate: Long
    ): ArrayList<Weekday> {
        var startDate = Date(startDateInUTCFormat)
        var endDate = Date(endDateInUTCFormat)
        var startDateCalendar = dateToCalendar(startDate)
        var endDateCalendar = dateToCalendar(endDate)
        val calendarDays = createListWithCalendarDates(startDateCalendar, endDateCalendar)


        return createListWithWeekDaysAndCorrespondingRecipes(calendarDays)

    }

    fun dateToCalendar(date: Date): Calendar {
        var calInstance = Calendar.getInstance()
        calInstance.setTime(date)
        return calInstance
    }

    fun createListWithCalendarDates(
        startDateCalendar: Calendar,
        endDateCalendar: Calendar
    ): ArrayList<Calendar> {
        var listWithCalendarDates = arrayListOf<Calendar>()
        while (startDateCalendar <= endDateCalendar) {
            listWithCalendarDates.add(startDateCalendar.clone() as Calendar)
            startDateCalendar.add(Calendar.DATE, 1)
        }
        return listWithCalendarDates
    }

    fun createListWithWeekDaysAndCorrespondingRecipes(calendarDays: ArrayList<Calendar>): ArrayList<Weekday> {
        var dayOfWeekAsString: String
        var listWithDaysOfWeeksAndRecipes = arrayListOf<Weekday>()
        var flattenedListWithRecipes: List<Recipe>?
        for (i in 0 until calendarDays.size) {
            var dayOfWeekAsInt = calendarDays[i].get(Calendar.DAY_OF_WEEK)
            dayOfWeekAsString = when (dayOfWeekAsInt) {
                1 -> "Sunday"
                2 -> "Monday"
                3 -> "Tuesday"
                4 -> "Wednesday"
                5 -> "Thursday"
                6 -> "Friday"
                else -> "Saturday"
            } 
          

            var calendarDateInString =
                transformCalendarDateIntoRequiredStringFormat(calendarDays[i])
            var listWithDateAndCorrespondingRecipes: List<MealplanScheduleWithRecipes> =
                listOf() 


            var liveDatalistWithDateAndCorrespondingRecipes =
                mealPlanRepository.getMealplanScheduleWithRecipes(calendarDateInString)


            liveDatalistWithDateAndCorrespondingRecipes.observeForever() { list ->
                listWithDateAndCorrespondingRecipes = list



                var listWithRecipes = listWithDateAndCorrespondingRecipes?.map { it.recipes }
                flattenedListWithRecipes = listWithRecipes?.flatten()
                var wochentag = dayOfWeekAsString
                listWithDaysOfWeeksAndRecipes.add(Weekday(dayOfWeekAsString, flattenedListWithRecipes))

            }    


        }


        return listWithDaysOfWeeksAndRecipes
    }

    fun transformCalendarDateIntoRequiredStringFormat(calendarDate: Calendar): String {
        var year = calendarDate.get(Calendar.YEAR)
        var month = transformCalendarMonthFormatToCorrectMonth(calendarDate)
        var day = calendarDate.get(Calendar.DAY_OF_MONTH)
        return "$day" + "$month" + "$year"
    }

    fun transformCalendarMonthFormatToCorrectMonth(calendarDate: Calendar): String {
        var monthCalendarFormat = calendarDate.get(Calendar.MONTH)
        var monthCorrectFormat = when (monthCalendarFormat) {
            0 -> "1"
            1 -> "2"
            2 -> "3"
            3 -> "4"
            4 -> "5"
            5 -> "6"
            6 -> "7"
            7 -> "8"
            8 -> "9"
            9 -> "10"
            10 -> "11"
            else -> "12"
        }
        return monthCorrectFormat
    }

    fun datesAreReasonable(startDate: Long, endDate: Long): Boolean {
        return (startDate <= endDate)
    }


}

我的问题是传递给 RecyclerView 适配器的列表由 Weekday 对象组成,这些对象由工作日的名称和相应的食谱组成(请参阅顶部的数据 class “weekday”)。 在视图模型中的“createListWithWeekDaysAndCorrespondingRecipes”方法中,我在一个 for 循环中创建了这个列表,该循环获取给定日期及其相应食谱之间的所有工作日。 但是,食谱是通过 Room 数据库查询异步获取的 LiveData,而工作日的名称是在主线程中同步派生的。 但是最后,当我创建工作日 object (请参阅 for 循环末尾的 listWithDaysOfWeeksAndRecipes.add(Weekday(dayOfWeekAsString, flattenedListWithRecipes) )时,我需要同时将它们放在一起。我还没有找到如何协调的方法这成功了。目前,将 object 添加到列表中的逻辑位于异步“observeForever”块中。请参见此处:

liveDatalistWithDateAndCorrespondingRecipes.observeForever() { list ->
                listWithDateAndCorrespondingRecipes = list



                var listWithRecipes = listWithDateAndCorrespondingRecipes?.map { it.recipes }
                flattenedListWithRecipes = listWithRecipes?.flatten()
                var wochentag = dayOfWeekAsString
                listWithDaysOfWeeksAndRecipes.add(Weekday(dayOfWeekAsString, flattenedListWithRecipes))

            }    

这会产生错误的结果,可能是因为主线程和观察者线程之间的协调不起作用。 但是,如果我采用从观察者块中添加的逻辑,则由于查询的异步特性,带有配方的列表将为我提供 null。

我知道我把这个问题描述得很糟糕。 也许还有人掌握了它并可以提供帮助?

你应该尽量避免使用observeForever,我希望你在一个片段或一个活动中使用它,它实际上有一个你的观察者可以使用的生命周期范围。

你的观察者应该看起来像这样

liveDataList.observe(viewLifecycleOwner, { list ->
    // The way I do it at the moment I just set the recyclerViews adapter and layoutManager here
    // This is not the best way to do it, so please keep that in mind
    recyclerView.apply {
        adapter = MyAdapter(list)
        layoutManager = LinearLayoutManager(requireContext())
    }
})
// Or if used inside an activity
liveDataList.observe(this, {})

这样,您的观察者将附加到您的生命周期并与您的视图一起“死亡”。 每当该列表更改时,您将在 recyclerView 中显示所有实体。 但是,当您将 Array 与 LiveData 一起使用时,LiveData object 永远不会在您向该值添加一些内容时“更新”,因为该数组只是对数组开头的 memory 引用。

为了解决这个问题,每当您向阵列添加内容时,您需要刷新 LiveData object 以触发更新和所有观察者。

myLiveDataObject.value.add(someOtherObject)
myLiveDataObject.value = myLiveDataObject.value

myLiveDataObject.value = myLiveDataObject.value会触发所有观察者说有变化,烦我知道

如果您在 viewHolder 或适配器中使用它,只需将生命周期与列表一起传递

我对 kotlin 也很陌生,请记住这一点,我向您保证有更好的方法来做到这一点,但希望它有所帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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