Home > Software design >  How to add items to desired ViewHolder position when using multiple viewtypes in RecyclerView?
How to add items to desired ViewHolder position when using multiple viewtypes in RecyclerView?

Time:10-02

enter image description here

I drew what I'm trying to implement.

To explain the function, it is a workout log app.

Image means that both RoutineItem and DetailItem can be added and deleted by button.

(I didn't include a picture of a button that adds a RoutineItem in the image!)

I made a function that adds both RoutineItem and DetailItem, but I couldn't create a function that adds DetailItem to each RoutineItem last.

In other words, when the button of each RoutineItem is pressed, a DetailItem should be added to the end of each RoutineItem, but my current implementation is that any RoutineItem button is always added to the end of the List when the button is pressed.

I'm not sure how to figure out the end of each RoutineItem because I'm implementing it using Multiple ViewHolder.

How do I know the last DetailItem listed after each RoutineItem?

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

Adapter

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

CodePudding user response:

I don't like the way you have handled this because these things do not belong to ViewModel and they belong to the Adapter, but in your code, you can add this function to your ViewModel to achieve what you want this method get the position of RoutineViewHolder and add a DetailViewHolder to end of it:

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

this method adds a new detail veiwholder to the end of the Routine viewholder. you can test it directly from where you use your viewmodel and for using this method in viewholder pass this method to viewholder and in viewHolder, set it in the onClickListener you want and pass getAdapterPosition() to it.

  • Related