I'm having this problem and spent hours exploring different solutions found here but couldn't figure it out. I have a RecyclerView with a RadioGroup (with two RadioButton) and an EditText. As expected, the text keeps getting duplicated on scroll and the "original" gets deleted. The same happens with the radio buttons. I've tried to save on another array backup the values when the view is recycled but couldn't solve the duplicating issue.
Here's my adapter
class ServicesCheckoutAdapter(var context: Context,
var servicesList: List<Service>) : RecyclerView.Adapter<ServicesCheckoutAdapter.ViewHolder>() {
private lateinit var onRadioGroupClickListener: OnRadioGroupClickListener
private lateinit var onTextChangedListener: OnTextChangedListener
private lateinit var onServiceClickListener: OnServiceClickListener
private var externalArray = mutableListOf<String>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.services_list_item,
parent, false)
val viewHolder = ViewHolder(view)
val position = viewHolder.adapterPosition
view.setOnClickListener {
if (onServiceClickListener != null) {
onServiceClickListener.onServiceClick(view, servicesList[position].id, position)
}
}
return viewHolder
}
override fun getItemId(position: Int): Long {
return super.getItemId(position)
}
override fun getItemViewType(position: Int): Int {
return super.getItemViewType(position)
}
interface OnServiceClickListener {
fun onServiceClick(view: View, serviceId: Int, position: Int)
}
fun setOnServiceClickListener(listener: OnServiceClickListener)
{
onServiceClickListener = listener
}
interface OnRadioGroupClickListener {
fun onRadioGroupClick(buttonId: Int, serviceId: Int, position: Int) {}
}
fun setOnRadioButtonClickListener(listener: OnRadioGroupClickListener) {
onRadioGroupClickListener = listener
}
interface OnTextChangedListener{
fun onTextChanged(position: Int, text: String)
}
fun setOnTextChangedListener(listener: OnTextChangedListener){
onTextChangedListener = listener
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Log.d("recycler", "lista: ${servicesList[position].serviceSolution}")
holder.edtSolution.removeTextChangedListener(holder.watcher)
holder.bind(context,
servicesList[position].id,
servicesList[position].name,
servicesList[position].serviceSolved,
servicesList[position].serviceSolution,
onRadioGroupClickListener,
onTextChangedListener,
position)
}
override fun getItemCount(): Int {
return servicesList.size
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var radioGroup = itemView.findViewById<RadioGroup>(R.id.radioGroupService)
val edtSolution = itemView.findViewById<EditText>(R.id.editTextCheckoutDesc)
var watcher: TextWatcher? = null
fun bind(context: Context,
serviceId: Int,
serviceName: String,
serviceSolved: Boolean,
serviceSolution: String,
onRadioGroupClickListener: OnRadioGroupClickListener,
onTextChangedListener: OnTextChangedListener,
position: Int
) {
itemView.findViewById<TextView>(R.id.serviceTitle)
.text = context.resources
.getString(R.string.service_title_comma, serviceName)
itemView.findViewById<RadioGroup>(R.id.radioGroupService)
.setOnClickListener {
onRadioGroupClickListener
.onRadioGroupClick(
(it as RadioGroup).checkedRadioButtonId, serviceId, adapterPosition)
}
if (serviceSolved) {
radioGroup.find<RadioButton>(R.id.radioBtnYes).isChecked = true
radioGroup.find<RadioButton>(R.id.radioBtnNo).isChecked = false
} else {
radioGroup.find<RadioButton>(R.id.radioBtnYes).isChecked = false
radioGroup.find<RadioButton>(R.id.radioBtnNo).isChecked = true
}
edtSolution.addTextChangedListener(object: TextWatcher{
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onTextChangedListener.onTextChanged(position, s.toString())
}
override fun afterTextChanged(s: Editable?) {
}
})
}
}
}
And here's the adapter initialization on the activity
serviceList = occurrence.services
servicesAdapter = ServicesCheckoutAdapter(this, serviceList)
recyclerViewServices.adapter = servicesAdapter
recyclerViewServices.layoutManager = LinearLayoutManager(this)
servicesAdapter.setOnRadioButtonClickListener(object : ServicesCheckoutAdapter.OnRadioGroupClickListener {
override fun onRadioGroupClick(buttonId: Int, serviceId: Int, position: Int) {
super.onRadioGroupClick(buttonId, serviceId, position)
when (buttonId) {
R.id.radioBtnYes -> {
serviceList[position].serviceSolved = true
servicesAdapter.notifyDataSetChanged()
}
R.id.radioBtnNo -> {
serviceList[position].serviceSolved = false
servicesAdapter.notifyDataSetChanged()
}
}
}
})
servicesAdapter.setOnTextChangedListener(object : ServicesCheckoutAdapter.OnTextChangedListener{
override fun onTextChanged(position: Int, text: String) {
serviceList[position].serviceSolution = text
}
})
I've tried to use a ListView but the keyboard went crazy changing focus. Decided to keep the RecyclerView and since my dynamic list isn't so large, I used the
holder.setIsRecyclable(false);
on the OnBindViewHolder() method and it solved my issue. Thanks everyone for the help