Android RecyclerView onBindViewHolder Called Twice (first time when adapter created) - android

I have simple RecyclerViewAdapter class:
class TariffsCardAdapter(context: Context, tariffCardItems: List<ItemsItem?>?) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var context: Context? = null
private var tariffCardItems: ArrayList<ItemsItem?>? = null
init {
this.context=context
this.tariffCardItems=tariffCardItems as ArrayList<ItemsItem?>
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_tariff_card_items, parent, false)
return TariffsCardAdapterViewHolder(view)
}
override fun getItemCount(): Int {
return tariffCardItems!!.size
}//getItemCount ends
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val viewHolder = holder as TariffsCardAdapterViewHolder
Log.e("posX","pos:::".plus(position))
}
private inner class TariffsCardAdapterViewHolder : RecyclerView.ViewHolder {
var text: TextView? = null
constructor(row: View) : super(row) {
text= row.findViewById(R.id.text) as TextView
}
}
}
Now lets suppose that my tariffCardItems: List<ItemsItem?> will have only 5 items in the list that I want to show.
The problem is that when the adapter gets instantiated that is for the first time the onBindViewHolder method is called twice.
So that the Log values prints as:
pos:::0
pos:::1
pos:::0
pos:::1
Which means that my recycler view position will be 1 in the end.
And the very first view will be created with list item at index 1 which is wrong and should be `0.
In other words, the very first child of the recycler view is created by 1 position value which is wrong.
What am I doing wrong?

It's simply because you are calling adapter 2 in code an recyclerview's bindviewholder should use to bind data not to call viewholder.
class TariffsCardAdapter(context: Context, tariffCardItems: List<ItemsItem?>?) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var context: Context? = null
private var tariffCardItems: ArrayList<ItemsItem?>? = null
init {
this.context=context
this.tariffCardItems=tariffCardItems as ArrayList<ItemsItem?>
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_tariff_card_items, parent, false)
return TariffsCardAdapterViewHolder(view)
}
override fun getItemCount(): Int {
return tariffCardItems!!.size
}//getItemCount ends
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
//bind data
}
private inner class TariffsCardAdapterViewHolder : RecyclerView.ViewHolder {
var text: TextView? = null
constructor(row: View) : super(row) {
text= row.findViewById(R.id.text) as TextView
}
}
}
For example your adapter should be like this
class CustomAdapter(val userList: ArrayList<User>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
//this method is returning the view for each item in the list
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_layout, parent, false)
return ViewHolder(v)
}
//this method is binding the data on the list
override fun onBindViewHolder(holder: CustomAdapter.ViewHolder, position: Int) {
holder.bindItems(userList[position])
}
//this method is giving the size of the list
override fun getItemCount(): Int {
return userList.size
}
//the class is hodling the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindItems(user: User) {
val textViewName = itemView.findViewById(R.id.textViewUsername) as TextView
val textViewAddress = itemView.findViewById(R.id.textViewAddress) as TextView
textViewName.text = user.name
textViewAddress.text = user.address
}
}
}
This method from your code is calling twice
val viewHolder = holder as TariffsCardAdapterViewHolder
Log.e("posX","pos:::".plus(position))

Related

Recyclerview override issue in onBindViewHolder , Not overiding

Override function onBindViewHolder is not overwriting when I add ViewHolder class inside it.
When I put viewHolder class outside it. it is not working.
Below in my code -
class data_custom_adapter(private val context: Context, private val datalist : ArrayList<Display_data>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomAdapter.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.display_data_card, parent, false)
return CustomAdapter.ViewHolder(v)
}
//Issue occur here -- it is not overriding when I add ViewHolder class
override fun onBindViewHolder(holder: data_custom_adapter.ViewHolder, position: Int) {
holder.bindItems(context,datalist[position])
}
override fun getItemCount(): Int {
return datalist.size
}
//the class is holding the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val datalist_data = itemView.findViewById(R.id.displayxmldata) as TextView
fun bindItems(context: Context, datashow : Display_data) {
datalist_data.text = datashow.appdata
}
}
}
Guys, please help to find out issue in this.
The ViewHolder must match the class that you indicate in the first line. so in
class data_custom_adapter(private val context: Context, private val datalist : ArrayList<Display_data>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
there's CustomAdapter.ViewHolder. Change this to your data_custom_adapter.ViewHolder and it should work. so like
class data_custom_adapter(private val context: Context, private val datalist : ArrayList<Display_data>) : RecyclerView.Adapter<data_custom_adapter.ViewHolder>() {
this needs to be also changed in the onCreateViewHolder function
I hope this will work.
class data_custom_adapter(
private val context: Context,
private val datalist : ArrayList<Display_data>) : RecyclerView.Adapter<data_custom_adapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.display_data_card, parent, false)
return data_custom_adapter.ViewHolder(v)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindItems(context,datalist[position])
}
override fun getItemCount(): Int {
return datalist.size
}
//the class is holding the list view
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val datalist_data = itemView.findViewById(R.id.displayxmldata) as TextView
fun bindItems(context: Context, datashow : Display_data) {
datalist_data.text = datashow.appdata
}
}
}

Is there any way make Android RecyclerView notifyDataSetChanged but don't call onCreateViewHolder

I make a recyclerView and update dataset with notifyDataSetChanged
and it will call onCreateViewHolder and onBindViewHolder
Is there any way just call onBindViewHolder but don't call onCreateViewHolder after notifyDataSetChanged?
Because i want to save some states in viewholder but still want to update view.
I try to use viewHolder.setIsRecyclable(false) but not works.
class SelectionAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var selectionList = mutableListOf<BetSelection>()
fun setData(selectionList: List<BetSelection>) {
this.selectionList = selectionList.toMutableList()
notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val viewBinding = SelectionItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val viewHolder = SelectionViewHolder(viewBinding, parent.context)
viewHolder.setIsRecyclable(false)
return viewHolder
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is SelectionViewHolder -> {
holder.setSelection(selectionList[position])
}
}
}
override fun getItemCount() = selectionList.count()
}
class SelectionViewHolder(
private val viewBinding: SelectionItemBinding,
private val context: Context
) : RecyclerView.ViewHolder(viewBinding.root) {
private var isOddsInitComplete = false
fun setSelection(selection: Selection) {
//isOddsInitComplete always false because onCreateViewHolder called everytime
if(isOddsInitComplete) {
updateOdds()
} else {
initOddsView()
isOddsInitComplete = true
}
.
.
.
}

How to implement two OnClickListener on two different views on RecyclerView kotlin?

I am working a RecyclerView item that has two views in it: a TextView and an ImagaView. I want to be able to click on both of them to cary out different functionalities.
How can I go about this
Here is my Adapter
class AddMeasurementAdapter(private val currentList:MutableList<DressMeasurementModel>, private val listener1: RecyclerClickListener, private val listener2: RecyclerClickListener): RecyclerView.Adapter<AddMeasurementAdapter.CardViewHolder>() {
//inner class
inner class CardViewHolder (itemView : View):RecyclerView.ViewHolder(itemView), View.OnClickListener{
val display:TextView = itemView.findViewById(R.id.measurement_recyclerview_item)
//Binding the data with the view
fun bind(dressMeasurementModel: DressMeasurementModel){
display.text = "${dressMeasurementModel.measurementName} ${dressMeasurementModel.measurement}"
}
init {
itemView.setOnClickListener(this#CardViewHolder)
}
override fun onClick(v: View?) {
val position: Int = adapterPosition
if (position != RecyclerView.NO_POSITION) {
listener1.onItemClick1(position, currentList)
}
if (position != RecyclerView.NO_POSITION) {
listener2.onItemClick2(position, currentList)
}
}
}
//Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.measurement_fragment_recyclerview_items, parent, false)
return CardViewHolder(view)
}
//Binding the view
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(currentList[position])
}
//Getting the item cout size
override fun getItemCount(): Int {
return currentList.size
}
}
Here is my interface
interface RecyclerClickListener {
fun onItemClick1(position: Int, currentList: MutableList<DressMeasurementModel>)
fun onItemClick2(position: Int, currentList: MutableList<DressMeasurementModel>)
}
You can add a listener in createViewHolder such as
//Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.measurement_fragment_recyclerview_items, parent, false)
view.textView.onClick{}
view.image.onClick{}
return CardViewHolder(view)
}
Okay, here is the Kotlin version of the answer.
In your bind method of viewholder,
fun bind(dressMeasurementModel: DressMeasurementModel) {
...
var position = adapterPosition
display.setOnClickListener {
listener.onTextClick(position)
}
image.setOnClickListener {
listener.onImageClick(position)
}
...
}
I later figured it So i want to share how i did it.
class AddMeasurementAdapter(
private val currentList: MutableList<DressMeasurementModel>,
private val listener1: RecyclerClickListener,
private val listener2: RecyclerClickListener
) : RecyclerView.Adapter<AddMeasurementAdapter.CardViewHolder>() {
// inner class
inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val display: TextView = itemView.findViewById(R.id.measurement_recyclerview_item)
val delete: ImageView = itemView.findViewById(R.id.measurementment_recyclerview_item_delete_button)
// Binding the data with the view
fun bind(dressMeasurementModel: DressMeasurementModel) {
display.text = "${dressMeasurementModel.measurementName} ${dressMeasurementModel.measurement}"
}
}
// Creating view
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.measurement_fragment_recyclerview_items,
parent,
false
)
return CardViewHolder(view)
}
// Binding the view and attaching the listener
override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
holder.bind(currentList[position])
holder.display.setOnClickListener {
listener1.onItemClickToEdit(holder.adapterPosition, currentList)
}
holder.delete.setOnClickListener {
listener2.onItemClickToDelete(holder.adapterPosition, currentList)
}
}
// Getting the item cout size
override fun getItemCount(): Int {
return currentList.size
}
}
When i assigned the onClickListener inside the fun bind(), the listener covered the the whole itemView layout and not the individual view inside the layout, hence only one click listener was achieved. The same thing applied inside the inner class.
Here is the interface
interface RecyclerClickListener {
fun onItemClickToEdit(position: Int, currentList: MutableList<DressMeasurementModel>)
fun onItemClickToDelete(position: Int, currentList: MutableList<DressMeasurementModel>)
}
By extending the interface in either activity of fragment, you have your onclickListener.
This article helped https://antonioleiva.com/recyclerview-listener/

Android: Values are not assigned when using data binding in my custom adapter

I'm trying to pass my binding variable for the adapter to the ViewHolder to assing values to the textviews in my adapter but the values are not assigned and the click listeners dont do anything.
Here's my adapter class:
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
private var binding: DoneAppointmentsAdapterBinding? = null
var activity = context
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val viewHolder = LayoutInflater.from(parent.context)
.inflate(R.layout.done_appointments_adapter, parent, false)
binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(viewHolder,binding, parent.context)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder(view: View, var binding: DoneAppointmentsAdapterBinding?, val context: Context) : RecyclerView.ViewHolder(view) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding?.donePatientItemName?.text = "${dataListItem.patientName}"
binding?.donePatientItemTime?.text = "Some date"
binding?.donePatientItemPatientInfo?.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding?.donePatientItemReviewCase?.setOnClickListener {
amendPrescriptionWarning(dataListItem,context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
You have messed up inside onCreateViewHolder . you have created view twice once with the LayoutInflater.from(parent.context) and once with DoneAppointmentsAdapterBinding.inflate . There should be only one in this case onlyDoneAppointmentsAdapterBinding .
class DoneAppointmentsAdapter(var context: DoneAppointmentsFragment, listener: ContentListener, var arrayList: List<Appointment>) :
RecyclerView.Adapter<DoneAppointmentsAdapter.ItemHolder>() {
private val listener: ContentListener = listener
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): ItemHolder {
val binding = DoneAppointmentsAdapterBinding.inflate(LayoutInflater.from(parent.context))
return ItemHolder(binding)
}
override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(arrayList, listener)
}
override fun getItemCount(): Int {
return arrayList.size
}
class ItemHolder (var binding: DoneAppointmentsAdapterBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(listOfData: List<Appointment>, listener: ContentListener) {
val dataListItem = listOfData[adapterPosition]
binding.donePatientItemName.text = "${dataListItem.patientName}"
binding.donePatientItemTime.text = "Some date"
binding.donePatientItemPatientInfo.setOnClickListener {
tempAppointmentId = dataListItem.id
listener.onPatientHistoryViewClicked(dataListItem.requestedBy)
}
binding.donePatientItemReviewCase.setOnClickListener {
amendPrescriptionWarning(dataListItem,binding.donePatientItemReviewCase.context)
}
binding.donePatientItemViewCase.setOnClickListener {
startPreview(dataListItem)
}
}
}
}
It should be like this . I have also removed extra useless arguments from ItemHolder .
To make it clear the problem with your code was RecyclerView.ViewHolder(view) you were passing view while you were doing all action and stuff on binding those holds are two different instances of View . Which should be fixed with above code.

Data from firebase realtime wont display on my recyclerView

I need to display data from firebase database using a recyclerView. everything works fine, I can see the cards and all but there wont be and data on the card.
here is my adapter class:
class MyAdapter(c: Context, t: ArrayList<Thoughts>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
var context : Context
var theThoughts : ArrayList<Thoughts>
init{
context = c
theThoughts = t
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.card_view01, parent, false))
}//end onCreateViewHolder
override fun getItemCount(): Int {
return theThoughts.size
}//end getItemCount
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
//holder.theTopicAdapter.text[position]
//holder.theThoughtAdapter.text[position]
//it works fine withOUT these two lines up
}//end onBindViewHolder
class MyViewHolder(itemView:View):RecyclerView.ViewHolder(itemView) {
var theTopicAdapter: TextView
var theThoughtAdapter: TextView
init{
//super.itemView
theTopicAdapter = itemView.textViewCard01
theThoughtAdapter = itemView.textViewCard02
}
fun MyViewHolder(itemView: View){
super.itemView
theTopicAdapter = itemView.textViewCard01
theThoughtAdapter = itemView.textViewCard02
}//end MyViewHolder function
}//end MyViewHolder class
}//end MyAdapter class
this is the code where I get the data and display it:
databaseReference.addValueEventListener(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()){
for (thought in p0.children){
//var theTopicGetter = thought.child("theTopic").value.toString()
val t: Thoughts = thought.getValue(Thoughts::class.java)!!
list.add(t)
}//end for loop
}//end if
adapter = MyAdapter(this#SeePreviousThoughts, list)
recyclerView.adapter = adapter
}//end onDataChange
override fun onCancelled(p0: DatabaseError) {
Toast.makeText(this#SeePreviousThoughts, "error, please try again.", Toast.LENGTH_SHORT).show()
}
})//end the listener
this is the class thoughs:
class Thoughts() {
lateinit var theID: String
lateinit var theCode: String
lateinit var theTopic: String
lateinit var theThought: String
constructor(theID: String, theCode: String, theTopic: String, theThought: String) : this() {
this.theID = theID
this.theCode = theCode
this.theTopic = theTopic
this.theThought = theThought
}
}
I should get the data from the database and it should be displayed on each card, but the cards are simply empty.
this is a screenshot of the empty recyclerView
the words topic and thoughts are default from the xml file.
I guess that my problem is that the function MyViewHolder in class MyViewHolder is never used... a screenshot of the code that isn't being used
Problem was in override fun onBindViewHolder(holder: MyViewHolder, position: Int) method. This adapter works:
class MyAdapter(val context: Context, val theThoughts: ArrayList<Thoughts>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.card_view01, parent, false))
}//end onCreateViewHolder
override fun getItemCount(): Int {
return theThoughts.size
}//end getItemCount
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.theTopicAdapter.text = theThoughts[position].theTopic
holder.theThoughtAdapter.text = theThoughts[position].theThought
}//end onBindViewHolder
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
lateinit var theTopicAdapter: TextView
lateinit var theThoughtAdapter: TextView
fun MyViewHolder(itemView: View){
super.itemView
theTopicAdapter = itemView.textViewCard01
theThoughtAdapter = itemView.textViewCard02
}//end MyViewHolder function
}//end MyViewHolder class
}//end MyAdapter class

Categories

Resources