I can see setting adapter.
setHasStableIds(true);
will improved RecyclerView performance, since it is improving lots of performance, why it is not default enabled in RecyclerView, Is there any limitations while setting setHasStableIds(true)?
The reason why it is not the default is because the data backing the adapter might not have stable Id's and the RecyclerView does not know this.
Yes there is a limitation one using setHasStableIds(true) - your data has to have stable Id's
You either need override getItemId(int position) to return a unique Id that would represent each Data Item, simply return the HashCode if you cannot think of anything better.
Or
If using something like a CursorAdapter this automatic has stable Id's as it uses the database table _id column.
The reason is when we are going to just displaying the data in recycler-view that case no need to unique id of each item and no animation required. so it's default its false.
But when we are going to perform some action in the dataset and update the recycler-view that case its need unique id for particular item and refreshing animation (blinking) it need.so we set as true
Related
I am doing a side project of making an app (with Java since I already know it). I have a recyclerview which loads some data via the room database library. The elements of the recyclerview are clickable.
My problem is I want the user to be able to sort the recyclerview so that the most recently accessed items go to the top.
My original idea was to assign the entities to have two variables - a String list_name which also serves as the id, and an Int order_of_access. Also, in my ViewModel I have a getAllLists method which returns a livedata list. I have an onChanged listener in the fragment activity which nicely updates the recyclerview when data is added/removed.
When the user adds a new list, it is assigned an order_of_access of the listsize (+1). But when the user deletes a group of lists, or clicks on a list, I want to update the order_of_access, say with an updateOrderAccess method.
Do you think this is the best way of doing what I want?
Where should I place updateOrderAccess and how would you recommend it be written? Since the method getAllLists returns livedata, it is tempting to put updateOrderAccess in an observer in the fragment (in onChanged) - but this will obviously create an infinite loop. It seems more in the correct philosophy to put it in the ViewModel, but then how would you suggest the updateOrderAccess method to be written? I'm having some trouble conceptualising what I need.
I hope the question is not too vague - I will update it if you need more details.
Where should I place updateOrderAccess and how would you recommend it
be written?
I am so sure that you must write it in the view model, as long as updateOrderAccess() is editing the list which is observable then you have andexpose by that the ui state then you have to put it in view model, and the observers will be notified ( in this case it is recycle view) and it will redraw the list in the order you offered.
note: do not you ever update the state(ui data) outside the state holder so you implement UDF (unidirectional Data Flow) pattern.
see the references below to read more about UDF so you never get confused where to declare your functions by letting the architicture lead you:
Guide to app architecture
ui layer
state holders and ui state
Do you think this is the best way of doing what I want?
i am not very sure that i got exactly what your app do, but it seems like you want to re-order the elements of recycle view depending on the ui event (click) or data change (deleting or adding new element), now you have two choices:
if the order is very importnat to you that much you want to keep it even if the app has been destroyed
then you have to add a field in the room entity represent the ordering (let us call it order) and whenever the user click on the recycle view you have to update the rooms field "order" which is "flow" or "liveData" or any observable type, that will tell the view model that there is a changing in the data, now the view model have to re-order the new data by the field "order" and pass it to the recycle view to show it.
if your app do not have to save the order changes after the app been destroyed
then you can simply do that:
create list which is called "orderedList" you will put the list items in it by the right order, and another list called "unorderlist" which have getAllLists
for the first case where the ordering is being changed by user click, you
can declare a function in viewModel then use it in the ui
controller (your activity or fragment), so whenever the list item is
clicked this function just re-order the orderedList elements ( which
is observable, so the changes reflect on the ui ) just by change the
clicked item position to the front of the list.
for the second case where the ordering changes by data changes like
add or delet a list item in the database, then you have to compare
the legnth of orderlist and unorderlist legnth, if unorderList is
longer then it is an add situation else it is a delete situation, in
adding case just add the last item of unorderList to the orderList,
else you have to check the deleted item and delete it from
orderList.
private fun turnOnAllItems() {
items.forEachIndexed { index, item ->
val viewHolder = recyclerView.findViewHolderForAdapterPosition(index)
as SwitchableItemViewHolder
viewHolder.switchButton.isChecked = false
}
}
What this does, is it also changes list items object values isEnabled to false. Looks weird to me, as I actually change viewHolder attribute. Why is this happening? How to avoid this?
I strongly believe that you are doing it the wrong way. RecyclerView is meant to display already modified data, meaning that you have a set of it.
Let's say, 10 tables in restaurant, and at some point table #4 becomes available for new customer and you want to indicate that.
A good approach would be to modify your list of tables somewhere outside RCV, even fragment or activity will do, and then just graphically update (all or just one) item by means of RCV.
Here's a little article I made to illustrate how to properly use RecyclerView, hope it will help you
I have an API which returns JSON and I would like to load an Image from a URL which is provided by this API. The Image should be passed into Adapter for a Recycling View.
Right now all Items which contain an Imgae_URL are getting skipped by my Adapter and I dont really understand why.
if (json_img_url.isNotEmpty()) {
Executors.newSingleThreadExecutor().execute({
val conn = URL(json_img_url).openConnection()
conn.connect()
val iStream:InputStream = conn.getInputStream()
val img_bitmap:Bitmap? = BitmapFactory.decodeStream(iStream)
newItems.add(Item(....img_bitmap))
})
....
itemArrayAdapter.addItems(newItems)
URL :"https://s3.us-east-2.amazonaws.com/c...."
The URls used are valid and Images on the S3 Bucket are all public.
The If statment returns true (I checked with Log.d) but the Item does not appear on the Phone, I dont recive an error and the app does not crash its just like the Item was never there...
I know there are librarys like Picasso or Glide but even with them I could not make it work and to be honest I would like to accomplish this task without a having to install an extra package, it just feels wrong.
Unlike ListView, there is no way to add or remove items directly through the RecyclerView adapter. You need to make changes to the data source directly and notify the adapter of any changes.There are many method available to use when notifying the adapter of different changes:
notifyItemChanged(int pos) : Notify that item at position has changed.
notifyItemInserted(int pos): Notify that item reflected at position has been newly inserted.
notifyItemRemoved(int pos): Notify that items previously located at position has been removed from the data set.
notifyDataSetChanged(): Notify that the dataset has changed. Use only as last resort.
Every time we want to add or remove items from the RecyclerView, we will need to explicitly inform to the adapter of the event. Unlike the ListView adapter, a RecyclerView adapter should not rely on notifyDataSetChanged() since the more granular actions should be used. See the API documentation for more details.
Also, if you are intending to update an existing list, make sure to get the current count of items before making any changes. For instance, a getItemCount() on the adapter should be called to record the first index that will be changed.
// record this value before making any changes to the existing list
int curSize = itemArrayAdapter.getItemCount();
// update the existing list
newItems.add(Item(....img_bitmap));
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
itemArrayAdapter.notifyItemRangeInserted(curSize, newItems.size());
I am new to android and got stuck when I click on an item in RecyclerView where the data set gets changed and position doesn't match with the ID in SQLite.I know we can get unique ID by using 'setHasStableID' but I was little confused as to where do I need to set this 'setHasStableId(true)' condition? How does this work?
The setHasStableIds(true) is to be applied to the adapter of RecylerView.
adapter.setHasStableIds(true);
Also for this to be taken effect you must have to override getItemId(int position), to return identified long for the item at position. We need to make sure there is no different item data with the same returned id.
The id can be an id from the database which will be unique for each item and are not changed throughout.
//Inside the Adapter class
#Override
public long getItemId(int position) {
return itemList.get(position).getId();
}
This will reduce the blinking effect on dataset notify, where it modifies only items with changes.
And the great part is it will add cool animations on item position changes!.
To solve blinking issue, we need to reuse the same ViewHolder and view for the same item. because
RecycleView disabled stable IDs by default.
So generally after
notifyDataSetChanged(), RecyclerView.Adapter didn’t assigned the
same ViewHolder to original item in the data set.
So solution is :-
setHasStableIds(true) :-
set setHasStableIds(true); in RecyclerView.Adapter .
true means this adapter would publish a unique value as a key for
item in data set.
Adapter can use the key to indicate they are the
same one or not after notifying data changed.
override getItemId(int position) :-
Then we must override getItemId(int position),to return
identified long for the item at position
We need to make sure there
is no different item data with the same returned id.
For new readers:
Note that for the Paging 3.0 library stable IDs are unnecessary and therefore unsupported.
Reference: https://developer.android.com/reference/kotlin/androidx/paging/PagingDataAdapter#getitemid
If aViewHolder's itemViewhassetActivated(.)called, that information is carried on after the view is recycled (i.e. the next will also be activated if the previous was).
Where would be a good place to save and store this information per item in the adapter rather than per itemView in the recycler; make items in the adapter a separate holder with an item and a boolean and then save it inonViewRecycled(.)as well as conditionally callsetActivated(.)again inonBind(.)?
Or are there better approaches altogether? (I was thinkingListViewmight be more intuitively, but unlessRecyclerViewis strongly discouraged for this typ of task, I'd prefer to use it.)
Well, I ended up creating a small local and private class that holds the status as well as the object. For now the status only includes a boolean isSelected, but I suppose it could be expanded and turned into a full fledged class in its own, when needed.