RecyclerView source code has empty onChange method - java

I'm wondering what does notifyDataSetChanged() actually do.
I was trying to navigate source code so I found it calling notifyChanged() method in a static member class that called AdapterDataObservable
notifyChanged() loops through data and call onChange() which is empty body.
So I can see nothing notifyDataSetChanged() did, how does my view change?

A method setAdapterInternal() is called when either setAdapter or swapAdapter are called, and it actually registers your RecyclerView as observer of changes in the adapter.
The data changes in your adapter, the adapter is the one responsible for watching your dataset, and it just let's the view know when the data changes, to adjust it's layout or bounds or whatever is needed to account for the data changes.

Related

How to disabling checkboxes inside recyclerview android

I have list/rows of checkboxes inside recycle view. In my case, the user can only select 3 checkboxes out of many. As soon as the user selects the third checkbox, I want to disable rest of the checkboxes in different rows.
I am capturing selecting/deselecting checkboxes inside adapter class.
As far as I know, I can enable/disable checkboxes inside onBindViewHolder class.
But in my case, I want to enable/disable checkboxes, after they have been rendered.
One of the approaches I can think is, to call notifyDataSetChanged(); from Activity class and then rerender all the recycled view.
But I hope there is the better way than doing this, inside the adapter class itself.
ps: I am new to android/java.
notifyDataSetChanged() will defently work in your case.
Please note that you don't have to call this method from the Activity.
You can call it internally within your adapter after the third checkbox was selected.
From the top of my head, One way to do this would be to store the "checked" positions indide an array within the adapter, then inside onBindViewHolder() check to see if the current position should be enabled or disabled.
In your model class you can have a boolean variable that decide whether the item should be enabled or not and another boolean variable to keep track of the checked state, or if the item is checked or not. In onBindViewHolder check the variable to enable or disable the item.Keep track of the selected items and then after every check and uncheck, check if the selected items exceeds the limit. Then loop through your items and then check if the item is checked or not. if the item is not checked set the value to false and after the loop send a notifyDataSetChanged() and in onBindViewHolder() check for the value and enable or disable the item/checkbox
You can use SparseBooleanArray to record all the changes for each item based on their positions.
You can use the following method in RecyclerView adapter:
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
Read more at RecyclerView.Adapter.
Only use notifyDataSetChanged() as the last resort. Because calling notifyDataSetChanged() will force the views to rebind and relayout. As in the documentation says:
void notifyDataSetChanged ()
Notify any registered observers that the data set has changed.
There are two different classes of data change events, item changes
and structural changes. Item changes are when a single item has its
data updated but no positional changes have occurred. Structural
changes are when items are inserted, removed or moved within the data
set.
This event does not specify what about the data set has changed,
forcing any observers to assume that all existing items and structure
may no longer be valid. LayoutManagers will be forced to fully rebind
and relayout all visible views.
RecyclerView will attempt to synthesize visible structural change
events for adapters that report that they have stable IDs when this
method is used. This can help for the purposes of animation and visual
object persistence but individual item views will still need to be
rebound and relaid out.
If you are writing an adapter it will always be more efficient to use
the more specific change events if you can. Rely on
notifyDataSetChanged() as a last resort.

Update RecyclerView adapter item without calling OnCreateViewHolder

how to update ReyclerView adapter without make OnCreateViewHolder(...) being called?
Because when i do
mRecyclerView.getAdapter().notifiItemChanged(position)
it goes inside OnCreateViewHolder(...) what i don't want to. The reason why i don't want is because I have expanded item view and onCreateViewHolder(...) will reinflate it so make collapsed. I only want to OnBindViewHolder(...) being called
Have anyone faced with it?
UPD:
Just found that OnCreateViewHolder(...) is called only one first time, in all next times it's not being called. What is the reason?
t
You could add a boolean isExpanded to your data objects in your dataset, and then in the onBindViewHolder method check whether isExpanded is true, if so expand the view programmatically. (and ofcourse don't forget to switch the boolean when expanding/collapsing the view)
This is the whole point of RecyclerView : it handle a pool of view holders, which are created once (calling OnCreateViewHolder()) then reused (or recycled, hence the view name) as much as possible via OnBindViewHolder().
OnCreateViewHolder() is used to create the views, while OnBindViewHolder (re)set the views state (like text content, selected state, ...)
It follows that any state associated with an item (like the expanded / collapsed state) should not be stored in the ViewHolder. You can for example use a private List in the adapter.

Isn't notifyDataSetChanged enough to force an adapter to redisplay the items it holds?

In a direct subclass of PagerAdapter in construction I pass in an empty List<String> that is the list to use for its functionality.
Then in another background task I fetch/create the strings that should be passed to the derived adapter and from my fragment I do: derivedAdapter.notifyDataSetChanged().
Inside my derivedAdapter I have overriden this method as follows:
#Override
public void notifyDataSetChanged() {
items.clear();
items.addAll(getCurrentCreatedList());
super.notifyDataSetChanged();
}
But the view pager, which I have set this adapter does not display anything. Am I misunderstanding something about the adapters and notifyDataSetChanged()?
Note: If I start the adapter with a full list, everything works perfectly
Update:
If I also recreate the adaptor at that point and reset it it also works fine. Only updating via notifyDataSetChanged does not seem to work.
Perhaps I am misunderstanding what it is supposed to do?

How to redraw only one view in Adapter

Is it somehow possible to force adapter to redraw only specific item?
Currently if I want to redraw the list, I call notifyDataSetChanged() which re-iterates through all the data-set and redraws all elements. What if I know exactly which element was updated and I want to redraw only that one?
notifyDataSetChanged() is not for redrawing elements, it is for notifying the adapter, that the elements have changed in some way, for example there are more elements, or their data is set from an other class etc. Of course, the ListView (or other class), to which the adapter is connected will redraw the elements, becuase the Adapter is forced to tell him, its dataset is changed.
As Yume117 commented, for redrawing you can call one View's invalidate() method (or even better postInvalidate()),
postInvalidate():
Cause an invalidate to happen on a subsequent cycle
through the event loop.
public void invalidate ()
Added in API level 1
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will be called at some point in the future. This must be called from a UI thread. To call from a non-UI thread, call postInvalidate().
You can call these methods on one view in the Adapter's getView() method, or only call invalidate on the ListView (or other view) itself, and it will only redraw the visible elements.
You can create singletone object with Map. And then put every View of your object to this map. And after all, you can change this object, redraw your specified View (you have to know position) and then call notifyDataSetChanged().
There is no such method out of box, which is not so bad as not whole dataset gets re-iterated and not all elements gets redrawn, but just currently visible ones. These are usually not so many.

adapter.notifyDatasetChanged() without changing current view

Is there a way to add views to a ViewPager (by adding data and calling notifyDatasetChanged() on its PagerAdapter) without changing the view currently being displayed?
I want the dataset to update in the background while the user is viewing other views. My problem now is that, when it finishes updating, it goes back to displaying the initial view. How can I prevent it from doing so?
As far as i know if you call notifyDatasetChanged() it only notifies the pager adapter that there is new data, the data has to be there before. so i think you should just not call notifyDatasetChanged() and when the user goes to the next view, the pageradapter loads the current (new) data. What about this solution?

Categories

Resources