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

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.

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

checked checkbox become unchecked in recyclerview after scrolling

I have a recyclerview with a list of items , when i check an item and scroll until the checked checkbox disppear , and scroll again to it , the checkbox become unchecked
this is my adapter code
class ContactsAdapter(var list:ArrayList<contact>, private val listener: (contact) -> Unit):RecyclerView.Adapter<ContactsAdapter.viewHolder>() {
inner class viewHolder(view:View):RecyclerView.ViewHolder(view)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): viewHolder {
var view = LayoutInflater.from(parent.context).inflate(R.layout.item_ui, parent, false)
return viewHolder(view)
}
override fun onBindViewHolder(holder: viewHolder, position: Int) {
var itemPosition = list[position]
holder.itemView.apply {
contact_name.text=itemPosition.contactName
contact_number.text=itemPosition.contactNumber[0]
check_number.setOnClickListener {
listener(itemPosition)
}
check_number.isChecked = itemPosition.checked
}
}
override fun getItemCount(): Int {
return list.size
}
}
A RecyclerView recycles its rows. Your setOnClickListener() needs to do something to hold onto which items are checked and unchecked, and your onBindViewHolder() needs to update the CheckBox when it binds an item. It seems like the second part is implemented, but perhaps not the first part. You may need to update itemPosition.checked in your lambda for setOnClickListener().

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 add onClickListener to the items of the recyclerView in Kotlin?

I am using recyclerView to show data from firebase database and I want to handle clicks,
Now the important part is that I want to know the number that was clicked in order to test google play in app billing before showing the next activity
I mean user should click item number one then pay to see information number 1 and so on
Any help, please ?
//my adapter
class MyAdapter(
private val arrayList: ArrayList<Long>
) :
RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.layout_item, parent, false)
return MyViewHolder(view)
}
override fun getItemCount() = arrayList.size
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.number.text = arrayList[position].toString()
}
class MyViewHolder(view: View) :
RecyclerView.ViewHolder(view) {
val number = view.findViewById<View>(R.id.singleNumberId) as TextView
}
}
Here is a small example I have of registering a click for a RecyclerView adapter item:
class PatientListAdapter : ListAdapter<Patient, PatientListAdapter.PatientViewHolder>(co.za.abcdefgh.viewmodels.PatientListViewModel.DiffItemCallback) {
// this property will be used to set the onclick callback for the entire adpater
var onPatientSelectedCallback: PatientSelectedCallback? = null
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PatientViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_patient, parent, false) as View
return PatientViewHolder(view)
}
override fun onBindViewHolder(holder: PatientViewHolder, position: Int) {
holder.view.item_patient_name.text = getItem(position).toString()
holder.view.item_patient_folderNumber.text = getItem(position).folderNumber
// lets set our on click for each viewholder here
holder.view.item_patient_info_card.setOnClickListener {
// the secret sauce .... getItem(holder.adapterPosition)
onPatientSelectedCallback?.onPatientSelected(getItem(holder.adapterPosition))
}
}
class PatientViewHolder(val view: View) : RecyclerView.ViewHolder(view)
// interface which defines a method signature that will called when a item in the adpater is selected
interface PatientSelectedCallback {
fun onPatientSelected(patient: Patient)
}
}
and then wherever you use the adapter after instantiating simply do:
val viewAdapter = PatientListAdapter()
viewAdapter.onPatientSelectedCallback =
object : PatientListAdapter.PatientSelectedCallback {
override fun onPatientSelected(patient: Patient) {
// do something with the chosen item
patientViewModel.setPatient(patient)
}
}

OnBindViewHolder is triggered only once

What is Happening: Even though there are two elements in val list: ArrayList<StudentModel> collection as seen in image below and I have explicitly given size 2 in getItemCount(). Only once onBindViewHolder is triggered
AdapterCode
class ListAdapter(private val list: ArrayList<StudentModel>,val context: Context) : RecyclerView.Adapter<MovieViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
return MovieViewHolder(LayoutInflater.from(context).inflate(R.layout.frag_disp_group_blocks, parent, false))
}
override fun getItemCount(): Int {
Timber.i("$list.size")
return 2
}
override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
val movie = list[position]
Timber.i("$movie")
Timber.i("$movie.get(position)")
holder?.tvAnimalType?.text = ""
}
}
class MovieViewHolder (view: View) : RecyclerView.ViewHolder(view) {
// Holds the TextView that will add each animal to
val tvAnimalType = view.list_title
}
passing a context from activity/fragment is not necessary unless they serve the purpose of their usage. Use context from the onCreateViewHolder parameter parent.
return MovieViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.frag_disp_group_blocks, parent, false))
This was a mistake from my side .... I had given the matchParent params so it was triggering once. wrapContent did solve the problem

Categories

Resources