Search code examples
androidkotlinandroid-recyclerviewandroid-checkbox

Recyclerview notifyDataSetChanged is not working?


I have a checkbox and a recyclerview in a layout. In the adapter I created functions: selectAll & unSelectAll. I expect all items to be checked/unchecked whenever checkbox(in fragment layout) is checked/unchecked respectively. However nothing seems to happen? I am not sure if it's the notifyDataSetChanged() that is not working or if am using it wrongly. I have checked every possible solution here but i unable not to find one. Any help is appreciated.

enter image description here

In adapter:

class DetailRecyclerAdapter :
  RecyclerView.Adapter<DetailRecyclerAdapter.BindingHolder>() {

  var details: List<ApiDetail>? = null
  var isSelectedAll: Boolean = true

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder {
    val layoutInflater = LayoutInflater.from(parent.context)
    val binding = ItemDetailBinding.inflate(layoutInflater, parent, false)
    return BindingHolder(binding)
  }

  override fun onBindViewHolder(holder: BindingHolder, position: Int) {
    val detail = details?.get(position)
    holder.binding.detail = detail

    if (isSelectedAll) {
      holder.binding.checkBox.isChecked = true
    } else if(!isSelectedAll) {
      holder.binding.checkBox.isChecked = false
    }
    
  }

  override fun getItemCount(): Int {
    return details?.size ?: 0
  }

  fun selectAll() {
    isSelectedAll = true
    notifyDataSetChanged()
  }

  fun unSelectAll() {
    isSelectedAll = false
    notifyDataSetChanged()
  }

  class BindingHolder(var binding: ItemDetailBinding) : RecyclerView.ViewHolder(binding.root)
}

In fragment:

    val adapter = DetailRecyclerAdapter()
    
    binding?.checkBox?.setOnCheckedChangeListener { _, isChecked ->
      if (isChecked) {
        adapter.selectAll()
      } else {
        adapter.unSelectAll()
      }
    }


binding?.detailsRecyclerview?.apply {
  layoutManager = LinearLayoutManager(context)
  adapter = DetailRecyclerAdapter()
}

In Layout:

<androidx.recyclerview.widget.RecyclerView
          android:id="@+id/details_recyclerview"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:overScrollMode="never"
          app:search_details="@{viewModel.details}"/>
       

BindingAdapter

  @BindingAdapter(value = ["search_details"])
  @JvmStatic
  fun RecyclerView.setSearchConditionDetails(apiDetails: ObservableField<List<ApiDetail>>) {

    adapter?.apply {
      this as DetailRecyclerAdapter
      this.details = apiDetails.get()
      notifyDataSetChanged()
    }

  }

Solution

  • This is the problematic code:

    binding?.detailsRecyclerview?.apply {
      layoutManager = LinearLayoutManager(context)
      adapter = DetailRecyclerAdapter()
    }
    

    You are effectively creating a 2nd instance of the DetailRecyclerAdapter, i.e. DetailRecyclerAdapter() instantiated twice.

    The code should read:

    val detailRecyclerAdapter = DetailRecyclerAdapter()
        
        binding?.checkBox?.setOnCheckedChangeListener { _, isChecked ->
          if (isChecked) {
            detailRecyclerAdapter.selectAll()
          } else {
            detailRecyclerAdapter.unSelectAll()
          }
        }
    
    
    binding?.detailsRecyclerview?.apply {
      layoutManager = LinearLayoutManager(context)
      adapter = detailRecyclerAdapter
    }
    

    You want to set your detailsRecyclerView.adapter to the instance you created earlier, not a new instance. I renamed the var from adapter to detailRecyclerAdapter to make this clearer.