RecyclerView DiffUtil need to update the data manually? - android

now I find a bug in my project
val diffCallback = diff.newInstance(list, newList)
val diffResult = DiffUtil.calculateDiff(diffCallback)
list.clear()
list.addAll(newList)
diffResult.dispatchUpdatesTo(this)
this is my diff code in my adapter, you can see I clear old list and update the new list.
but I use data in viewholder and set tag with it in a view.
when I set old data as a tag in a view, then I diff the list, because as this diffUtil return true, so view also handler old data in his tag, but I always update adapter list when diff, so when I use adapter.list.getindex(data) I get -1 because view tag is old data, and my adapter list had to update new list, Even if the old list data view == new list.
when I remove list.clear() list.addAll(newList), the diff cannot succeed,
someone can tell me why? and how can I solve this problem?

Related

NotifyDataSet Changed Android - Recycler View Adapter

val recyclerView = findViewById<RecyclerView>(R.id.rv)
val adapter = UserDetailsAdapter(users)
recyclerView.adapter =adapter
recyclerView.layoutManager= LinearLayoutManager(this)
Thread{
userDao.insertAll(user,user2,user3)
users= userDao.getAll() as ArrayList
Log.d("Checking Service",users.size.toString())
recyclerView.post {
Log.d("Checking Service","Data set Changed")
adapter.notifyDataSetChanged()
}
}.start()
Here recycler view in the UI does not updating the items .
runOnUIThread also tried but not working.
kindly explain why notify data set changed not working. and tell me how to update recycler view adapter from another thread.
You need to update the users data inside the adapter before actually calling notifyDataSetChanged().
In your case you need to update the data source which you initialised in adapter constructor .
add this function to your adapter instead of calling notify adapter change from activity
fun updateAdapter(users: ArrayList<User>) {
this.users= users
notifyDataSetChanged()
}
and call this function
adapter.updateAdapter(users)

DiffUtil.Callback() not working as the same as notifyDataSetChanged()

I have a message list that contains messages. And I used a regular RecyclerView.Adapter instead of ListAdapter.
ChatFragment
chatViewModel.msgListDisplayLiveData.observe(viewLifecycleOwner, {
chatAdapter.setChat(it)
})
Here is the function to update messageList in adapter.
ChatAdapter
fun setChat(newMessages: List<Message>) {
val diffUtil = ChatDiffUtil(messageList, newMessages)
val diffResults = DiffUtil.calculateDiff(diffUtil)
messageList = newMessages
diffResults.dispatchUpdatesTo(this)
--> Method of using notifyDataSetChanged()
// messageList = newMessages
// notifyDataSetChanged()
}
Previously I am using notifyDataSetChanged() and the list item can be updated instantly, but without any animation.
Thus, I decided to use DiffUtils() and I got the nice animation when my list's item got append/remove.
However, when I want to update one of the item, DiffUtils seems do not have any visible effect.
Using notifyDataSetChanged() - can instantly update the row.
Using DiffUtils() - cannot update instantly.
What had I done wrongly? Please advise me.

How to update RecyclerView using Kotlin and Room?

I am trying to display data from my Room database on RecyclerView, it works fine except when I fill in new data and try to update my RecyclerView it doesn't update. Here's part of the code that I tried using:
MainActivity.kt
val adapter = MainAdapter(myData)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = adapter
MainAdapter.kt
fun refresh(data: List<NewList>) {
val currentList: MutableList<MyList> = mutableListOf()
currentList.clear()
currentList.addAll(data)
notifyDataSetChanged()
}
I am trying to call this function from Fragment file so it can pass updated list from Room Database (It updates list once someone opens that fragment)
Fragment.kt
var myData = App.instance.db.getAllDao().getAll()
val adapter = MainAdapter(myData)
adapter.refresh(myData)
I printed updated list in the updateData() function and it does show updated list, as well as tried printing ItemCount and it also shows updated ItemCount, but does not update actual display with new data.
The adapter attached to the RecyclerView in MainActivity and in fragment are not same.
just delete adapter creation in Fragment and pass RecyclerView instance to Fragment and then set layout manager and adapter there instead of activity. lastly call updateData method in order to be able to populate adapter.

RecyclerView update multiple locations

The notifyItemChanged method can only update one location at a time,and The notifyItemRangeChanged method can only update consecutive positions,How can I update multiple separate locations at once?
There is a really useful recyclerView item update blog here. Check it and use appropriate solution for your scenario. https://medium.com/#suragch/updating-data-in-an-android-recyclerview-842e56adbfd8
You can use notifyItemRangeChange when updating the data from and to in the position adapter
This example shows how to use it:
List<String> data;
data = new ArrayList<>();
//Add Anything list data
void AddMultipleItems() {
ArrayList<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("orange");
fruits.add("banana");
int fruitsIndex = 2;
data.addAll(insertIndex, fruits );
adapter.notifyItemRangeChanged(insertIndex, fruits.size());
}
Use this method if you have multiple data changed but not all , those changed data also are in cluster so that you can say from 25th to 30th index data are changed .
If all data are changed call :
notifyDataSetChanged();
Notes:
If you use notifyDataSetChanged(), then no animation will be performed. This can also be an expensive operation, so it is not recommended to use notifyDataSetChanged() if you are only updating a single item or a range of items.
Check out this link if you are making large or complex changes to a list.
In my case I had to update only two items, i.e., new and old item.
I put the old items position in a variable and update both new and old positions as below,
class FontAdapter(
private var curSelectedFontSource: Int
) : ListAdapter<FontItem, FontAdapter.MyViewHolder>(Companion) {
private var oldItemPos = -1
// other implementations
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val isSelected = getItem(position).fontSource ==
curSelectedFontSource
if (isSelected) {
oldItemPos = position
}
holder.binding.root.setOnClickListener {
notifyItemChanged(position)
notifyItemChanged(oldItemPos)
}
}
}

How to restore to the previous state in RecyclerView after loading more data

I have been working with RecyclerView for a while. I am following lazy loading, so I am showing 10 data on the view each time. If user scroll to the bottom, the page re-load from the TOP! however, I want to stay where it was previously! So, I have tried to use
recyclerView.scrollToPosition(position);
However, this breaks the UI flow!
My second try is using onSaveInstanceState() and onRestoreInstanceState(Bundle state); However, that does not work! My page is re-loads to the top!
Parcelable state = layoutManager.onSaveInstanceState();
layoutManager.onRestoreInstanceState(state);
I have tried every other methods, apparently none is working for me!
Any help would be highly appreciated! Thanks in advance!
Note : Make sure that you are not initialising or calling setAdapter() method each time after updating your dataset. If not, then
You have to update your data list and call notifyDataSetChanged() which will update your adapter from existing position.
Let's say you have stored your data into ArrayList mData;
Your getItemCount() would be
#Override
public int getItemCount() {
if (mData != null && mData.length() > 0)
return mData.size();
return 0;
}
Now create one more method in your adapter which you will called each time whenever you will get new data from server. This method will simply override your dataset and will update your adapter
private void updateDataSet(ArrayList<String> mData){
this.mData = mData;
notifyDataSetChanged();
}
I have done this functionality before 2 days ago
I share my idea with you
Make
Listview lv; //Assume Find view by Id
List<Model> models = new ArrayList();
Now Make an Adapter and assign blank models
CustomAdapter adapter = new CustomeAdapter(context,models);
lv.setAdapter(adapter);
Now when you have new data to load with lazylodaing
do this
models.addAll(newModels); //new ModelList
adapter.notifyDataSetChanged();
Thats it.
This is the default behaviour Recycler View to recycle/resuse views. As in official docs:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
If you are having any view related issues then save your view state in your List<Object> like setting visible item or not per position. And in your onBindViewHolder method as per position show/hide your view.

Categories

Resources