RecyclerView notifyItemChanged call onBindViewHolder also on not visible cells - android

I have a vertical RecyclerView with LinearLayoutManager with different item types and one ViewHolder. It is a messaging screen.
I want show/hide message date on click on the cell.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layout = R.layout.xxx
val viewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false))
viewHolder.itemView.layout?.setOnClickListener {
setMessageDateVisibility(viewHolder)
}
return viewHolder
}
So i added the method to reload view with notifyItemChanged(pos)
private fun setMessageDateVisibility(holder: ViewHolder) {
val pos = holder.adapterPosition
if (pos.toLong() == THREAD_DEFAULT_POSITION)
return
val message = mDataset[pos]
message.isDateVisible = !message.isDateVisible
notifyItemChanged(pos)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val message = mDataset[position]
itemView.message_date.text = message.date
itemView.message_date.visibility = if (message.isDateVisible)
View.VISIBLE
else
View.GONE
}
But the problem a the end, is that because of this line notifyItemChanged(pos), the override onBindViewHolder method called for all not visible cells and so my screen is lagging with 150/200 cells
Can you help me to find a solution ?

Related

View all button to button of recycler view

Initially load 3 items from list after click on view all button load remaining all data to same page in kotlin.
To show 3 items I added limit to show data in adapter class in getItemCount mathod. So now I am able to see only 3 items from list. But not able to do like ad all data on same page on click view all button.
Here is how I have solved the issue.
class MyAdapter(private val maxItems: Int = 3) : RecyclerView.Adapter<MyViewHolder>() {
private val items = mutableListOf<String>()
fun setData(data: List<String>) {
items.clear()
items.addAll(data)
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return min(items.size, maxItems)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(items[position])
}
fun updateMaxItems(max: Int) {
maxItems = max
notifyDataSetChanged()
}
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val textView = itemView.findViewById<TextView>(R.id.item_text)
fun bind(text: String) {
textView.text = text
}
}
In your Activity/Fragment where you set up the RecyclerView, create a button "View All" and set its onClickListener
val viewAllButton = findViewById<Button>(R.id.viewAllButton)
viewAllButton.setOnClickListener {
//update the adapter's maxItems to show all items
adapter.maxItems = Int.MAX_VALUE
adapter.notifyDataSetChanged()
}

How to get the current loaded rows/items of a RecyclerView

I have a list of items in my RecyclerView and I need to get the currently loaded items (i.e. not the recycled or off screen items) at a time.
This is may adapter
class MyAdapter(list: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var items: List<String> = list
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val myText = view.findViewById<TextView>(R.id.my_text);
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val listItem: View = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.myText.text = items[position]
}
override fun getItemCount() = items.size
}
And RecyclerView:
recyclerView.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
hasFixedSize()
adapter = MyAdapter(listOf("a", "b", "c")) // List is bigger than that
}
How can I do that?
I'd a case that I needed to ensure how many RecyclerView items are currently loaded in order to control a performance issue; so that created this for someone else was looking for an answer.
You need to track the list of loaded & recycled items:
The items are loaded in onBindViewHolder() callback
The items are recycled in onViewRecycled() callback
So, we can create a local list that tracks the current loaded item positions, add new positions in onBindViewHolder() and remove recycled ones in onViewRecycled()
In the below code the currentLoadedPositions tracks the loaded positions, and you can create a method that returns it:
class MyAdapter(list: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private var items: List<String> = list
// Tracking the currently loaded items in the RecyclerView
private val currentLoadedPositions: ArrayList<Int> = ArrayList()
fun getLoadedPositions(): ArrayList<Int> {
return currentLoadedPositions
}
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val myText = view.findViewById<TextView>(R.id.my_text)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val listItem: View = LayoutInflater.from(parent.context)
.inflate(R.layout.list_item, parent, false)
return MyViewHolder(listItem)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = items[position]
holder.myText.text = item
currentLoadedPositions.add(position)
Log.d("LOG_TAG", "onViewRecycled: $currentLoadedPositions")
}
override fun getItemCount() = items.size
override fun onViewRecycled(holder: MyViewHolder) {
currentLoadedPositions.remove(Integer.valueOf(holder.adapterPosition));
Log.d("LOG_TAG", "onViewRecycled: $currentLoadedPositions")
}
}

How to change bg recyclerview specific position [ Kotlin ]

Q1
I make some recyclerview with cardview I want to click some position and change bg current position to black and another position to green in the adapter
Q2
I want to change something specific position in the adapter
class RecyclerAdapterBrand(private var imageList: List<Int>) :
RecyclerView.Adapter<RecyclerAdapterBrand.ViewHolder>() {
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val imageView: ImageView = itemView.findViewById(R.id.imgViewBrand)
val cardView: CardView = itemView.findViewById(R.id.cardViewBrand)
init {
// Set Recycler Click
itemView.setOnClickListener { v: View ->
val positionClick: Int = (adapterPosition + 1)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(
R.layout.recy_brand_layout,
parent,
false
)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.post {
holder.imageView.setImageResource(imageList[position])
}
}
override fun getItemCount(): Int {
return imageList.size
}
You can change the background of the clicked item easily.You have to create a variable called selectedposition and assigned to -1. Whenever you click the item you have to set the clicked position value to selectedPosition.And call notifyDataSetChanged() method on recyclerview's item ClickListener method. Then in OnBindViewHolder() method you have to check if the position & selected position are same, set the background color to green.otherwise set the background color to black.
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var selectedPosition=-1
val imageView: ImageView = itemView.findViewById(R.id.imgViewBrand)
val cardView: CardView = itemView.findViewById(R.id.cardViewBrand)
init {
// Set Recycler Click
itemView.setOnClickListener { v: View ->
selectedPosition=(adapterPosition + 1)
val positionClick: Int = (adapterPosition + 1)
notifyDataSetChanged()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(
R.layout.recy_brand_layout,
parent,
false
)
return ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if(selectedPosition==position){
holder.itemView.setBackground(#green color)
}else{
holder.itemView.post {
holder.imageView.setImageResource(#black color)
}
}
}
override fun getItemCount(): Int {
return imageList.size
}
I have included those lines in your adapter class.I hope it will solve your problem.

Strange animation when removing item from RecyclerView

I have a recyclerView populated by the val microphones: MutableList<Microphone>. There's a button in the row layout to remove that item from the list, which I've managed to get functioning, but the animation is going wrong. It looks like when the recyclerView updates, it removes the last item in the list, then the correct item, and then the whole list below the removed item animates to the correct state.
Here's the relevant bits of the recyclerView adapter:
class RecyclerViewAdapter(
val microphones: MutableList<Microphone>
): RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val cellForRow = LayoutInflater.from(parent.context).inflate(R.layout.favorites_row, parent, false)
return ViewHolder(cellForRow)
}
override fun getItemCount(): Int {
return microphones.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val microphone = microphones[position]
holder.bind(microphone)
holder.favoritesButton.setOnClickListener {
microphones.remove(microphone)
notifyItemRemoved(position)
}
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
val modelText = view.findViewById<TextView>(R.id.model_textView)
val brandText = view.findViewById<TextView>(R.id.brand_textView)
val favoritesButton = view.findViewById<ImageButton>(R.id.btnFavorite)
fun bind(
microphone: Microphone,
) {
modelText.text = microphone.model
brandText.text = microphone.brand
}
}
}
Here's a screen recording of the issue:
https://imgur.com/a/pJt3KwH

RecyclerView adapter `onCreateViewHolder` & `onBindViewHolder` invoked only once

I have a RecyclerView.Adapter like this:
internal class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
private val data: List<MyModel> = SeedData().seed()
override fun onCreateViewHolder(v: ViewGroup, viewType: Int): MyViewHolder {
val binding = MyListitemBinding.inflate(LayoutInflater.from(v.context), v, false)
return MyViewHolder(binding)
}
override fun getItemCount() = data.size
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(data[position])
}
}
However, only the first item from the data is getting displayed (i.e. onCreateViewHolder & onBindViewHolder invoked only one time). How can I make it display all items from the data properly?
The commented answers above are correct. My list item (views) were full height of screen:
This means that RecyclerView would only update the ViewHolder once you scroll to the next element. The solution is to modify the height of these items.

Categories

Resources