Reputation: 2465
I have the following recyclerview adapter:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.add_new_card_list_item.view.*
import kotlinx.android.synthetic.main.bank_card_list_item.view.*
import kz.moneyman.R
class BankCardsAdapter(
private val onDeleteCardClickListener: OnDeleteCardClickListener,
private val onAddCardClickListener: OnAddCardClickListener
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var checkedItem = 0
private val differ = AsyncListDiffer(this, BankCardsDiffUtilCallback())
override fun getItemViewType(position: Int): Int {
if (differ.currentList[position].isNotEmpty()) {
return R.layout.bank_card_list_item
}
return R.layout.add_new_card_list_item
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return if (viewType == R.layout.bank_card_list_item) {
BankCardViewHolder(itemView)
} else {
AddNewCardViewHolder(itemView)
}
}
override fun getItemCount(): Int {
return differ.currentList.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (differ.currentList[position].isNotEmpty()) {
(holder as BankCardViewHolder).bindData(differ.currentList[position], onDeleteCardClickListener)
} else {
(holder as AddNewCardViewHolder).bindData(onAddCardClickListener)
}
}
fun setData(cardNumbers: ArrayList<String>) {
differ.submitList(cardNumbers)
}
fun deleteElement(position: Int) {
differ.currentList.removeAt(position)
notifyDataSetChanged()
}
interface OnDeleteCardClickListener {
fun onDeleteCardClicked(position: Int, value: String)
}
interface OnAddCardClickListener {
fun onAddCardClicked()
}
inner class BankCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindData(value: String, onDeleteCardClickListener: OnDeleteCardClickListener) {
itemView.card_number_tv.text = value
itemView.card_firm_iv.setImageResource(if (value[0] == VISA_START_SYMBOL) R.drawable.ic_visa else R.drawable.mastercard)
itemView.delete_card_tv.setOnClickListener {
if (adapterPosition != RecyclerView.NO_POSITION) {
onDeleteCardClickListener.onDeleteCardClicked(adapterPosition, value)
}
}
itemView.bank_card_rb.setOnCheckedChangeListener(null)
itemView.bank_card_rb.isChecked = checkedItem == adapterPosition
itemView.bank_card_rb.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
checkedItem = adapterPosition
notifyDataSetChanged()
}
}
itemView.bank_card_cl.setOnClickListener {
checkedItem = adapterPosition
notifyDataSetChanged()
}
}
}
inner class AddNewCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bindData(onAddCardClickListener: OnAddCardClickListener) {
itemView.online_add_card_tv.setOnClickListener {
if (adapterPosition != RecyclerView.NO_POSITION) {
onAddCardClickListener.onAddCardClicked()
}
}
}
}
companion object {
private const val VISA_START_SYMBOL = '4'
private const val MASTERCARD_START_SYMBOL = '3'
}
}
So, as you can see, I set data in method setData
and then data is passed to diffutil, where the computations take place. But then I need to remove an element of the differ
list in adapter. For this purpose I created method deleteElement
, which removes an element at position. But when I run the app, I catch the following exception:
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.remove(Collections.java:1359)
at views.adapters.BankCardsAdapter.deleteElement(BankCardsAdapter.kt:54)
at TestStepFragment$onViewCreated$1.onDeleteCardClicked(TestStepFragment.kt:36)
at BankCardsAdapter$BankCardViewHolder$bindData$1.onClick(BankCardsAdapter.kt:76)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
As I understand, the problem is that differ.currentList is immutable list, but how can I solve this problem?
Upvotes: 6
Views: 6886
Reputation: 607
You must not remove it from within the adapter, but from your Activity/Fragment/any_ui_element you're using.
If you're supplying a list, then you will edit the supplied list from your UI (simplified version):
class Fragment() {
var listSupplied = mutableListOf(1,2,3)
var adapter : BankCardsAdapter? = null
override fun onCreate() {
...
adapter = BankCardsAdapter(
onDeleteCardClickListener{ deleteElement(element) },
onAddCardClickListener{ clickElement(element) }
)
adapter.submitList(listSupplied)
...
}
private fun deleteElement(element: Element) {
listSupplied.remove(element)
adapter.notifyDatasetChanged()
}
However, I would advise you to use ListAdapter and then take advantage of its amazing methods for adding/removing specific elements, which will also add some animations to it and also save on computations as it doesn't edit the whole list but just the requested elements.
Take a look here: https://medium.com/simform-engineering/listadapter-a-recyclerview-adapter-extension-5359d13bd879
After you do that, what you'll need to call is:
adapter?.notifyItemRemoved(viewHolder.adapterPosition)
Upvotes: 2
Reputation: 157487
you have to keep copy of the original data, remove the item from there and then call differ.submitList
. DiffUtils
will do the rest for you
Upvotes: 8