Knowing when a view gets dirty in RecyclerView - android

How do I get a reference to the ViewHolder of the item in the RecyclerView that has been marked dirty? RecyclerView.OnChildAttachStateChangeListener just gives a reference to the view.
I want to know when the view is dirty and not recycled. See the official definition of the two below:
Recycle (view): A view previously used to display data for a specific
adapter position may be placed in a cache for later reuse to display
the same type of data again later. This can drastically improve
performance by skipping initial layout inflation or construction.
Scrap (view): A child view that has entered into a temporarily
detached state during layout. Scrap views may be reused without
becoming fully detached from the parent RecyclerView, either
unmodified if no rebinding is required or modified by the adapter if
the view was considered dirty.
Dirty (view): A child view that must be rebound by the adapter before
being displayed.

By dirty, do you mean recycled? If so then the following listener can help you.
See RecyclerListener

Related

How to understand RecyclerView properly

I have watched a lot of videos on RecyclerView but I am very confused on whether the ViewHolder is an adapter that changes the view or does it really just represent each item that is displayed on the screen.
How can I understand the concept more?
Let's see if this helps.
The general job of any list-style view would be to display a long chain of views, each representing a piece of data, most likely from a list.
Now imagine we consider the simplest implementation, where it draws all those views when it is created and allows you to scroll through them. This is obviously very inefficient for performance as a long list would require a lot of processing all at once.
RecyclerView aims to solve this and only creates enough views to fit on the screen and when scrolling, changes the content of those views seamlessly to reflect more data.
Now these views are created initially as empty blueprints and the RecyclerView wraps them inside something called a ViewHolder, which can not only hold the view but also pointers to different parts of the view, which saves doing even more work every time new data is displayed. Then initially and when scrolling, the RecyclerView 'binds' the relevant data to view holders.
The job of the adapter is to tie this process together and has three methods that require you to provide a concrete implementation:
getItemCount - expected to return how many items there are in the full dataset
onCreateViewHolder - create a view holder representing a generic row
onBindViewHolder - bind data to a view holder, therefore updating the rows content when given the view holder and the position in the dataset that should be bound

Android Listview: Updating invisible views or disabling recycling

I am designing an android game, and I'm trying to use ListView. The list uses a BaseAdapter, and is filled with an ArrayList. When the user pressed the ok button, I scroll to the top of the list, and then iterate through each soldier in the list. I set the background drawable of one of the child views of the soldier so that it's a short animation that displays "hit" or "miss". I used a Handler.postDelayed() so that each animation for the one before it to finish.
The problem is that I cannot modify the views that are invisible. I will have up to 13 soldiers in my list at a time, but only a maximum of 5 can be displayed. So once I hit the sixth soldier, I get a null pointer exception from using ListView.getChildAt(soldierArrayIndex). My solution was to add smoothScrollTo(soldierArrayIndex) before the getChildAt() call so that it would become visible, but the problem persisted.
So my question isn't exactly "how to fix my code?". I'm more wondering if there is a way to disable the recycling that ListView does. The reason I'm using getChildAt() is because I need to use findViewById() on the view it returns, and then modify the view that was found by ID. However, if the view was never recycled, getChildAt() wouldn't return null.
Another idea I had was to just use a scroll view, enter 13 instances of the Soldier View that I created, and then set the ones I'm not currently using to "gone". My only problem is I don't know how to iterate through those views.
TL;DR: How to update views that are not currently visible in listview (they are currently "recycled")?
I think you are confusing Model and View here.
Adapter Views take an adapter that usually has access to the whole dataset. So even though your ListView will only render as many items as are visible on the screen, you should still be able to access your off-screen items by calling getItem(position) on your adapter.
It seems like you are calling getView directly on your adapter.
I’m suggesting you shouldn’t do this since this is not how AdapterViews and Adapters are designed. Instead, you need to indicate to your ListView when something in your model is changed (via notifyDataSetChanged) and leave it up to your ListView to call your adapter’s getView method on your behalf.
I imagine the sequence of events being something like this:
Update Soldier Object
Call notifyDataSetChanged on your adapter which will tell your ListView it needs to redraw stuff
ListView iterates through the visible Soldier items calling getView(int position, View convertView, ViewGroup parent) on each.
Your getView implementation in your adapter gets the Soldier at the specified position and determines what the View should look like at that point in time.
For "offscreen" Soldiers (ones that are not visible in the scroll area of the ListView,) there is no need for the ListView to render them, so it will not call getView on those positions.
However, if a Soldier that was offscreen is now scrolled into view, the ListView will call your adapter's getView method with that Soldier's position in your array.
Finally, if you want finer-grained control over how an AdapterView should update itself, you might consider RecyclerView as it allows you to notify changes on an item by item basis.

android custom view in expandablelistview with adapter goes nuts

I have a serious problem with a custom view i use in an expandableListView. I use an adapter that extends BaseExpandableListAdapter. The custom view changes its state depending on if it is being consulted or modifying. The state change involves animation and show/hide of ui parts.
The problem is that even thought i let only two views, when i click on the second view i order to make it change its state, the adapter calls the getViewGroup fo the two views but systematically inverting the corresponding model data ids.. .witch makes the ui to animate again...
I precise that i don't use the viewholder pattern since the custom view has its own internal references to the controls to be updated.
Any idea would be appreciated?
i found out the solution, everytime the model changes i called notifyDataChanged() which makes the entire listview to be redrawn

Get cached views from ListAdapter

When a list is shown and scrolled around, ListAdapter.getView() either creates a new list item view, or retools an existing view passed to it (as "convertView) to display new content.
I'm wondering, if there's a way to get all the views created by getView()? E.g. if the list item views needs to free some resource when the activity is destroyed, it would be nice to go through all list item views and tell them to release the resource.
Well, it's not as simple as iterating through the ListView using ListView#getChildAt(int) - the ListView is an AbsListView subclass - and AbsListView implements the RecycleBin used to hold Views that will be reused.
Before a View is placed in the RecycleBin its detached from its parent (the list view) using ViewGroup#detachViewFromParent(View), thus rendering it unaccessible using getChildAt(int).
What you should implement is the RecyclerListener interface that will notify you when a View is moved to the RecycleBin.
Since ListView is a ViewGroup, you should be able to iterate on child views like this:
for (int i=0; i < myListView.getChildCount(); i++) {
View v = myListView.getChildAt(i);
//Check to see if this view has the resource, then release it
}
Something similar was needed in this question: Android: Access child views from a ListView
It is java. You can't order the application or instance to be really destroyed so as to free resources. With finalize() you can only rise the probability that java machine will do it.
Unless your adapter is used outside of this Activity (in which case, you don't really know that you no longer need these views right?), your only reference(s) to this adapter is within the Activity. That means that if your Activity is garbage collected your adapter will be garbage collected, which means that your views will get collected. I wouldn't bother trying to optimize this unless you have a measurable problem with this behavior.

why does the ViewHolder pattern work?

I learned Android's ArrayAdapter today, and find there is a commom pattern which uses a ViewHolder to hold Views' reference instead of calling findViewById everytime.
But how does it work? Adapter is usually used to display a list of View(Group)s, If I cache the View, why don't they all reference to the oldest one?
If you want the best explanation on how the ViewHolder works, check out Romain Guy's Google I/O 2009 talk in youtube , specially the first 15 minutes.
In short, the Adapter functions as a link between the underlying data and the ViewGroup. It will render as many Views as required to fill the screen. Upon scrolling or any other event that pushes a View is out of the screen, the Adapter will reuse that View, filled with the correct data, to be rendered at the screen.
The getView(int pos, View view, ViewGroup parent) method will use the right View at any time, regardless of your layout. I do not know the internals of this, but I'm sure you can browse the source code for any adapter (such as ArrayAdapter.java) if you're interested.
The ViewHolder just keeps a pointer to the Views as obtained by view.findViewById(int id). It is the Adapter responsibility to return the right data corresponding to any position.
Slides 11 to 13 of Romain's presentation will make it a lot more clear than anything I can write.
Sorry but denis' answer may be wrong.
In fact, the view instances(and ViewHolders) are as many as your screen can display.
If your screen looks like:
[list view]
the first item
the second item
the third item
the fourth item
You will have 4 instances of views. If you scroll screen, the first will disappear but be pass to getItem() as convertView for you to create the fifth item.
So you can use the references in first ViewHolder.
I believe the work beneath the list view is something like this (considering we have only one item view type):
do once:
inflate item view from layout, cache it
repeat for every item:
ask adapter to fill the data into the view
draw the view on the screen
move to next item
so you have the view which is inflated from xml layout and can be reused for drawing multiple list items. ViewHolder speeds it up a bit more by saving getViewById lookups.

Categories

Resources