In my application I want use RecyclerView
adapter and for set data I used DiffUtils
.
I Want search data from server and then show it into RecyclerView
!
I write below codes, but after search data show items overlay!
I want first clear previous data, then add new items!
Fragment codes :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//InitViews
binding.apply {
searchEdt.addTextChangedListener {
val search = it.toString()
if (search.isNotEmpty()) {
viewModel.searchList.observe(viewLifecycleOwner) { response ->
lastMoviesAdapter.differ.submitList(response.data)
searchMoviesRecycler.initRecycler(LinearLayoutManager(requireContext()), lastMoviesAdapter)
}
viewModel.loadSearchMovies(search)
} else {
Snackbar.make(view, getString(R.string.fillAllFields), Snackbar.LENGTH_SHORT).show()
}
}
Adapter codes:
class LastMoviesAdapter @Inject constructor() : RecyclerView.Adapter<LastMoviesAdapter.ViewHolder>() {
private lateinit var binding: ItemHomeMoviesLastBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
binding = ItemHomeMoviesLastBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder()
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(differ.currentList[position])
holder.setIsRecyclable(false)
}
override fun getItemCount() = differ.currentList.size
inner class ViewHolder : RecyclerView.ViewHolder(binding.root) {
@SuppressLint("SetTextI18n")
fun bind(item: Data) {
binding.apply {
moviePosterImg.load(item.poster) {
crossfade(true)
crossfade(1000)
}
movieNameTxt.text = item.title
movieRateTxt.text = item.imdbRating
movieCountryTxt.text = item.country
movieYearTxt.text = item.year
}
}
}
private val differCallback = object : DiffUtil.ItemCallback<Data>() {
override fun areItemsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: Data, newItem: Data): Boolean {
return oldItem == newItem
}
}
val differ = AsyncListDiffer(this, differCallback)
}
How can I fix it?
create a diff utils like this (can do in adapter class)
class DiffUtilCallback(private val oldList: List<Any>, private val newList: List<Any>) :
DiffUtil.Callback() {
// old size
override fun getOldListSize(): Int = oldList.size
// new list size
override fun getNewListSize(): Int = newList.size
// if items are same
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return oldItem.javaClass == newItem.javaClass
}
// check if contents are same
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]
return oldItem.hashCode() == newItem.hashCode()
}
}
then create two methods in your adapter
// set data
fun setData(data: List<Any>) {
this.data = data.toMutableList()
}
// add new data
fun setNewData(newData: List<Any>) {
val diffCallback = DiffUtilCallback(data, newData)
val diffResult = DiffUtil.calculateDiff(diffCallback)
data.clear()
data.addAll(newData)
diffResult.dispatchUpdatesTo(this)
}
in fragment/activity do this
adapter = AdapterDual(recycler, lifecycleScope) // init adapter
adapter.setData(list) // set data
recycler.layoutManager = LinearLayoutManager(activity)
recycler.adapter = adapter // set adapter on recycler
// and when you load new data or replace it
adapter.setNewData(newList)
remember that if you are loading more items then
newList
will contain both the previous items as well as loaded items