Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Recyclerview notifyItemChanged(position ,payLoad) don't work if the view at that position is not visible on screen or scrolled out

I am using RecyclerView in my app, it has itemViewCache of 20 items because max possible size of my list would be 20. It is "Single Item Selection List". Now When the list loads, the first cell becomes highlighted as default selected position. If i click the cell at different position, the view at that position becomes selected & highlighted, simultaneously previously selected view becomes white ie unselected. Above works fine as I use adapter.notifyDataSetChanged(position, payLoad) method to update data in the list. But when the cell at currently selected position is scrolled out of the screen and I try to select new position, however the new cell grabs the highlighting and becomes selected, it does not makes previously selected cell white. For the cell at scrolled out position, system dont even call the notifyDataSetChanged(positioin, payLoad) method to make ui in it. Any help would be greatly appreciated as I work a lot with RecyclerViews.

Here is the code of my adapter :

class EventsCalendarAdapter(var eventSectionedFrag: EventSectionedFrag, var arrayList: ArrayList<String>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    val view = LayoutInflater.from(parent.context).inflate(R.layout.adapter_calendar_months, parent, false)
    return CalendarHolder(view)
}

override fun getItemCount() = arrayList.size


override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any>) {
    if (payloads.isNotEmpty()) {
        var bundle = payloads[0] as Bundle
        var isSelected: Boolean? = bundle.getBoolean("selected")
        var setCurrentMonthSelected: Boolean? = bundle.getBoolean("setMonth")


        with(holder) {
            if (isSelected != null) {
                val tvMonth = itemView.findViewById<TextView>(R.id.tv_month)
                tvMonth.apply {
                    setTextColor(ContextCompat.getColor(eventSectionedFrag.activity!!, if (isSelected) R.color.white else R.color.better_than_black))
                    background.setColorFilter(ContextCompat.getColor(eventSectionedFrag.activity!!, if (isSelected) android.R.color.holo_red_light else R.color.white), PorterDuff.Mode.SRC)
                }
            }

            if (setCurrentMonthSelected != null) {
                val view = itemView.findViewById<View>(R.id.view_current)
                view.visibility = if (setCurrentMonthSelected) {
                    View.VISIBLE
                } else {
                    View.INVISIBLE
                }
            }
        }
    } else {
        super.onBindViewHolder(holder, position, payloads)
    }
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val myHolder = holder as CalendarHolder
    myHolder.bindItems(eventSectionedFrag = eventSectionedFrag, month = arrayList[position], position = position)
}

class CalendarHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    fun bindItems(eventSectionedFrag: EventSectionedFrag, month: String, position: Int) {
        val tvMonth = itemView.findViewById<TextView>(R.id.tv_month)
        val cardMonth = itemView.findViewById<ConstraintLayout>(R.id.card_month)
        val view = itemView.findViewById<View>(R.id.view_current)
        view.visibility = View.INVISIBLE
        val callbacks = eventSectionedFrag as CalendarCallbacks

        tvMonth.text = month
        view.apply {
            visibility = if (callbacks.getCurrentMonthPosition() == position) {
                View.VISIBLE
            } else {
                View.INVISIBLE
            }
        }
        cardMonth.setOnClickListener {
            callbacks.onMonthSelected(adapterPosition, tvMonth)
        }
    }
}

fun addMonths(arrayList: ArrayList<String>) {
    this.arrayList.clear()
    this.arrayList.addAll(arrayList)
    notifyDataSetChanged()
}

interface CalendarCallbacks {
    fun onMonthSelected(position: Int, currentSelectedView: TextView)
    fun getCurrentMonthPosition(): Int
}

}

and here is how I am invoking the changes from my activity,

override fun notifyCalendar(isCurrentYearSelected: Boolean, position: Int) {
    var bundle = Bundle()
    bundle.putBoolean("setMonth", isCurrentYearSelected)
    calendarAdapter.notifyItemChanged(position, bundle)
}
like image 401
Pratik Mhatre Avatar asked Nov 07 '22 05:11

Pratik Mhatre


1 Answers

From the RecyclerView.Adapter#notifyItemChanged(int position, java.lang.Object payload) docs:

Adapter should not assume that the payload will always be passed to onBindViewHolder(), e.g. when the view is not attached, the payload will be simply dropped.

So when the cell at currently selected position is scrolled out of the screen, nothing will change because the old selected view is not exist.

You can modify your arrayList to store the selected status in it, or add a method to CalendarCallbacks which can fetch the current selected position, but you must take care of changing the selected position when needed.

like image 102
Hong Duan Avatar answered Nov 14 '22 23:11

Hong Duan