在 RecyclerView 中使用多个视图类型时如何将项目添加到所需的 ViewHolder 位置?

Posted

技术标签:

【中文标题】在 RecyclerView 中使用多个视图类型时如何将项目添加到所需的 ViewHolder 位置?【英文标题】:How to add items to desired ViewHolder position when using multiple viewtypes in RecyclerView? 【发布时间】:2021-11-22 15:58:22 【问题描述】:

我画了我想要实现的东西。

解释一下函数,就是workout log app

图像意味着RoutineItemDetailItem 都可以是added and deleted by button

(我没有包含在图片中添加RoutineItem 的按钮图片!)

我创建了一个同时添加RoutineItem and DetailItem 的函数,但我无法创建一个函数将DetailItem 添加到最后一个RoutineItem

也就是说,当每个RoutineItem的按钮被按下时,一个DetailItem应该被添加到每个RoutineItem的末尾,但是我目前的实现是任何RoutineItem按钮总是被添加到按下按钮时List 的结尾。

我不确定如何找出每个 RoutineItem 的结尾,因为我正在使用 Multiple ViewHolder 来实现它。

我如何知道每个RoutineItem 后面列出的最后一个DetailItem

RoutineItem.kt

sealed class RoutineItem() 
    data class RoutineModel(
        val workout: String, // excercise
        val unit: String, // unit (kg or lbs)
        var routineDetail: List<DetailModel> = listOf()
    ) : RoutineItem()

    data class DetailModel(
        val set: String,
        val reps: String = "1",
        val weight: String
    ) : RoutineItem()

ViewHolder.kt

sealed class RoutineItemViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) 
    class RoutineViewHolder(
        private val binding: ItemRoutineBinding,
        private val addDetailClicked: (Int) -> Unit,
        val deleteDetailClicked: (Int) -> Unit
    )
        : RoutineItemViewHolder(binding) 
        init 
            binding.add.setOnClickListener 
                addDetailClicked(adapterPosition)
            
        
        fun bind(item : RoutineItem.RoutineModel) 
            binding.workout.text = item.workout
        
    

    class RoutineDetailViewHolder(private val binding: ItemRoutineDetailBinding)
        : RoutineItemViewHolder(binding) 
        fun bind() 
            // EMPTY
        
    

适配器

class RoutineItemAdapter(
    private val addDetailClicked: (Int) -> Unit,
    private val deleteDetailClicked: (Int) -> Unit) :
    ListAdapter<RoutineItem, RoutineItemViewHolder>(RoutineDiffCallback2()) 

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoutineItemViewHolder 
        return when(viewType) 
            R.layout.item_routine -> RoutineItemViewHolder.RoutineViewHolder(
                ItemRoutineBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent,
                    false
                ),
                addDetailClicked,
                deleteDetailClicked
            )
            R.layout.item_routine_detail -> RoutineItemViewHolder.RoutineDetailViewHolder(
                ItemRoutineDetailBinding.inflate(
                    LayoutInflater.from(parent.context),
                    parent,
                    false
                )
            )
            else -> throw IllegalArgumentException("Invalid ViewType Provided")
        
    

    override fun onBindViewHolder(holder: RoutineItemViewHolder, position: Int) 
        when(holder) 

            is RoutineItemViewHolder.RoutineViewHolder -> holder.bind(currentList[position] as RoutineItem.RoutineModel)
            is RoutineItemViewHolder.RoutineDetailViewHolder -> holder.bind()
        
    

    override fun getItemCount(): Int = currentList.size // footer 때문에 +1

    override fun getItemViewType(position: Int): Int 
        return when(currentList[position]) 
            is RoutineItem.RoutineModel -> R.layout.item_routine
            is RoutineItem.DetailModel -> R.layout.item_routine_detail
            else -> throw IllegalArgumentException("Invalid ViewType Provided")
        
    

ViewModel

class WriteRoutineViewModel : ViewModel() 
    private var _items: MutableLiveData<List<RoutineItem>> = MutableLiveData(listOf())
    private val routines = mutableListOf<RoutineItem>()

    val items: LiveData<List<RoutineItem>> = _items
    
    fun addRoutine(workout: String) 
        routines.add(RoutineItem.RoutineModel(workout, "TEST"))
        routines.add(RoutineItem.DetailModel("1","3","3"))
        _items.postValue(routines)

    

    fun addDetail(pos: Int) 
        routines.add(RoutineItem.DetailModel("1","3","3"))
        _items.postValue(routines)
    

【问题讨论】:

试试环氧树脂。 (github.com/airbnb/epoxy) 使用方法 -> proandroiddev.com/… 【参考方案1】:

我不喜欢你处理这个的方式,因为这些东西不属于ViewModel,它们属于Adapter,但是在你的代码中,你可以在你的ViewModel中添加这个函数来实现你想要什么这个方法得到RoutineViewHolder的位置并在它的末尾添加一个DetailViewHolder

fun addDetailItemToEndOfRoutine(parentRoutineViewHolderPosition : Int)
  var addingPosition:Int = parentRoutineViewHolderPosition 
   for(position in (parentRoutineViewHolderPosition + 1) .. routines.size())
    if(routines.getOrNull(position) is RoutineItem.DetailModel)
     continue
    
    addingPosition = position  - 1
    break
   
 //now you have found the position and you can add the detail model you want
 items.add(addingPosition + 1, RoutineItem.DetailModel("1","3","3"))
 _items.postValue(routines)

此方法将一个新的细节视图添加到常规视图的末尾。 您可以直接从使用视图模型的位置对其进行测试,并在视图支架中使用此方法将此方法传递给视图支架和视图支架, 在你想要的 onClickListener 中设置它并将getAdapterPosition() 传递给它。

【讨论】:

以上是关于在 RecyclerView 中使用多个视图类型时如何将项目添加到所需的 ViewHolder 位置?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Firebase 中将多个 Recyclerview 设置为单个适配器(多视图类型)?

RecyclerView 中的多个视图

RecyclerView中的多个视图

每个ViewType中的MultipleView RecyclerView单选

一起滚动多个水平RecyclerView

从 RecyclerView Adapter Android 隐藏和显示多个视图持有者