ListAdapter in a ViewPager2 is calling onBindViewHolder twice every time submit list is called

Viewed 73

As title says. ListAdapter on a ViewPager2 that uses Multiple View Holder is calling onBindViewHolder twice after clicking a button which forcefully refreshes the data changing the model.isStylusInputEnabled from either true or false. Here are the logs for a single ViewHolder named FirstViewHolder notice that isStylusInputEnabled for both ViewHolder is different. Which causes a problem as it resets my UI back to false even if I set it to true

2022-04-19 08:23:07.437 8667-8667/com.sample.dev D/TEST: load onBindViewHolder model.isStylusInputEnabled true this FirstViewHolder{e930e86 position=0 id=-1, oldPos=-1, pLpos:-1 not recyclable(1) no parent}}

2022-04-19 08:23:07.496 8667-8667/com.sample.dev D/TEST: load onBindViewHolder model.isStylusInputEnabled false this FirstViewHolder{843d958 position=0 id=-1, oldPos=-1, pLpos:-1 scrap [changeScrap] tmpDetached not recyclable(3) no parent}}

ListAdapter Code

class MultipleViewAdapter(
val firstFlow: Flow<List<FirstModel>>,
val secondFlow: Flow<List<SecondModel>>,
val thirdFlow: Flow<List<ThirdModel>>) : ListAdapter<ParentModel, RecyclerView.ViewHolder>(DefaultDiffUtil<ParentModel>()) {

companion object {
    const val VIEW1 = 0
    const val VIEW2 = 1
    const val VIEW3 = 2
}

private val scope = CoroutineScope(Dispatchers.Main)

var onRemarksChangeListener: OnRemarksChangeListener? = null

fun interface OnRemarksChangeListener {
    fun onChange(position: Int, value: String)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return when(viewType) {
        VIEW1 -> FirstViewHolder(ItemFirstBinding.inflate(LayoutInflater.from(parent.context),  parent, false))
        VIEW2 -> SecondConvectionViewHolder(ItemSecondBinding.inflate(LayoutInflater.from(parent.context),  parent, false))
        else -> ThirdViewHolder(ItemThirdBinding.inflate(LayoutInflater.from(parent.context),  parent, false))
    }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    holder.setIsRecyclable(false)
    when(holder) {
        is FirstViewHolder -> holder.bind(position)
        is SecondViewHolder -> holder.bind(position)
        is ThirdViewHolder -> holder.bind(position)
    }
}


//necessary for onBindViewHolder to render position correctly
override fun getItemViewType(position: Int): Int {
    return getItem(position).pageType
}

//necessary for onBindViewHolder to render position correctly
override fun getItemId(position: Int): Long {
    return position.toLong()
}

fun onDestroy() {
    scope.cancel()
}


inner class FirstViewHolder(private val _binding: ItemFirstBinding) : RecyclerView.ViewHolder(_binding.root), StylusInput {

    private val _adapter by lazy {
        FirstAdapter()
    }

    override val pathCollection: List<List<Pair<Float, Float>>>
        get() = _binding.canvas.pathCollection

    override var isEnabled: Boolean = false
        set(value) {
            with(_binding) {
                container.isScrollable = !value
                canvas.isWritable = value
            }

            field = value
        }

    fun bind(position: Int) {
        val model = getItem(position)

       
        Log.d("TEST", "load onBindViewHolder ${model} model.isStylusInputEnabled ${model.isStylusInputEnabled} this ${this}}")

        isEnabled = model.isStylusInputEnabled
        _binding.canvas.loadPathCollection(model.stylusInput)


        _binding.rvFirst.adapter = _adapter
        scope.launch {
            firstFlow.collectLatest {
                _adapter.submitList(it) {}
            }

            _binding.rvFirst.viewTreeObserver.addOnDrawListener {
                _binding.canvas.refresh(model.stylusInput)
            }
        }
    }
}

inner class SecondViewHolder(private val _binding: ItemSecondBinding) : RecyclerView.ViewHolder(_binding.root), StylusInput {

    private val _adapter by lazy {
        SecondAdapter()
    }

    override val pathCollection: List<List<Pair<Float, Float>>>
        get() = _binding.canvas.pathCollection


    override var isEnabled: Boolean = false
        set(value) {
            with(_binding) {
                container.isScrollable = !value
                canvas.isWritable = value
            }

            field = value
        }

    fun bind(position: Int) {
        val model = getItem(position)
      
        isEnabled = model.isStylusInputEnabled
        _binding.canvas.loadPathCollection(model.stylusInput)


        _binding.rvSecond.adapter = _adapter
        scope.launch {
            secondFlow.collectLatest {
                _adapter.submitList(it)
            }

            _binding.rvSecond.viewTreeObserver.addOnDrawListener {
                _binding.canvas.refresh(model.stylusInput)
            }
        }
    }
}

inner class ThirdViewHolder(private val _binding: ItemThirdBinding) : RecyclerView.ViewHolder(_binding.root), StylusInput {

    private val _adapter by lazy {
        ThirdAdapter()
    }

    override val pathCollection: List<List<Pair<Float, Float>>>
        get() = _binding.canvas.pathCollection

    override var isEnabled: Boolean = false
        set(value) {
            with(_binding) {
                container.isScrollable = !value
                canvas.isWritable = value
            }

            field = value
        }

    fun bind(position: Int) {
        val model = getItem(position)
 
        isEnabled = model.isStylusInputEnabled
        _binding.canvas.loadPathCollection(model.stylusInput)

        _binding.rvThird.adapter = _adapter
        scope.launch {
            thirdFlow.collectLatest {
                _adapter.submitList(it)
            }

            _binding.rvThird.viewTreeObserver.addOnDrawListener {
                _binding.canvas.refresh(model.stylusInput)
            }
        }
    }
}

}

0 Answers
Related