I thought I understood the recycler mechanism of Listview... but it seems I didn't.
I have a ListView of 20 items, only 10 items is visible initially. Why Android calls getView() of positions like 15 or 17 (non visible rows)?
Why convertView parameter of visible rows passed to getView() are the same? I thought if convertView is not null, it refers to a row that isn't visible anymore.
After adding some logging in getView(), I couldn't explain what really happens.
convertView is a View instance that was previously returned by the getView method and is not visible anymore, its purpose is TO BE converted to a NEW ITEM assigned by the new position in the method. The adapter do it to reuse views and avoid inflating new ones. If it is calling the same visible row twice it can only be at listview inflation time (it happens).
To the first question, it inflates non-visible views so it will be available to be scrolled when you reach it.
YOU MUST: re-set all values in a convertView to a new item.
Related
I've got a strange situation here.
There's a list view with a custom adapter. The view has a few different item types which are correctly used in getViewItemType. On just about every device and supported OS the getView method in the adapter is called without any weird behavior.
I know getView can be called many times and that isn't the issue. On a Nexus 5 and Nexus 6, however, getView is called twice for the same item type and passes a convertView of null.
The result is we end up creating two views for the same row in the list. It seems that one will actually get attached or added to the listview while the other isn't.
Any suggestions, or tips that might be causing this?
How do you know called twice for same item?
I ran the app with a debugger. It stopped on my breakpoint in getView multiple times (as expected) but two of the stops for the item passed null convertViews. It is a very small list, only four rows, each a different item type. They all show on the screen easily
You should check the position in getView() to make sure if called twice.
I suppose not because that the first time of each item view(convertView) always null, then you need inflate the view with item view layout and return it. While next time show the same view(even though for different item), the convertView will be not null, but you still need set views with value that according to the item position.
Hope you got!
I am using list view to populate data in the custom dialog. I am passing a List to adapter. The list has 250 different items.
The problem I have is getView method of adapter is being called only 7 times and then the data is displayed in dialog. The dialog has first 7 items repeated to fill all the 250 rows in the dialog.
I couldn't understand why the getView is called only 7 times (and also in my dialog I can see 7 items at a time.. and i need to scroll to view other items. Is there any relation between the number of elements I see and number of times the getView will be called).
Any idea why it happens. Thanks in advance.
Yes, I think getView is called when the item is actually displayed on the screen. When you scroll, more items become visible and getView will be called more times.
i am creating new row only if convertView is null otherwise I return the same row. I couldn't understand why the data is duplicated in my dialog"
You can try to set the data of each item every time you return from getView method. convertView is reused, you need to update the data of the convertView bind of. You can use ViewHolder to save widgets in each item.
I have been using using listview since long but suddenly I experienced some of its random behaviour.
When I call invalidateViews() on listviews, in some cases it re-inflates the views for each row and in some cases it doesn't. This is completely random. Can you tell me exactly how does it works?
Ideally it should just refresh the data inside views(rows) and not inflate them again.
Thanks
Calling invalidateViews() on a ListView should not re-inflates the ListView item views. The only way to force the ListView to re-inflates its item views, meaning that the recycled views are cleared, is by resetting the ListView adapter ( I had to dig in the source code to discover that ).
If you take a look at the invalidateViews() method implementation (source code in AbsListView.java), you will see this (Android API 16) :
/**
* Causes all the views to be rebuilt and redrawn.
*/
public void invalidateViews() {
mDataChanged = true;
rememberSyncState();
requestLayout();
invalidate();
}
The method rememberSyncState() implemented in AdapterView.java stores the ListView's position of the selected item (if any), you can check it out.
The requestLayout() method takes care of the measuring, laying out, and drawing (as far as I know) so nothing here too.
And finally invalidate() is used to force the list view to draw (with the new measurements, ...)
So calling invalidateViews() should not force the list view to re-inflate its views.
Could you be re-setting the adapter somewhere so it re-inflates all the views ?
Actually, listview.invalidateViews() causes all the views to be rebuilt and redrawn. You can come to know this when you look on the description gets displayed in eclipse when you try to select invalidateViews().
adapter's getView() method has parameter convertView which is null first time when you populate a listview, later when you scroll/invalidate your listview convertView is not null so you can use it and dont need to inflate a new row
What is the correct way to modify all child elements (not only the visible ones) of a listview.
I have an image which is set, by default, to visibilty gone. I wish to make it visible after the user clicks a button (for all items).
Thanks!
What is the correct way to modify all child elements (not only the visible ones) of a listview.
One thing to understand about a ListView is that not all of the list items are generated (inflated/populated) at any given time.
Suppose, for example, your list Adapter has 1000 items in it but the ListView can only display 10 at once. It would be a very bad waste of resources (e.g., memory) to create all 1000 list items.
Instead, only the 10 visible items are created and each time you scroll one off the top or bottom of the screen, the one which has disappeared is re-cycled by being passed as convertView into the Adapter's getView method.
getView (int position, View convertView, ViewGroup parent)
To do what you are asking you should extend whatever Adapter type you wish to use and override the getView method. In that method check if convertView is null or not. If it is, inflate your own instance of your list item layout. If it is not null then re-use the UI elements (TextView, ImageView etc).
To have all ImageView elements visible, use a global Boolean such as showImageView which will be toggled by the button press. Then use that in getView to decide whether or not to set the visibility of the ImageView.
See Adapter.getView(...)
Probably you should set the image visibility in your ListAdapter's getView() depending on some field value. Upon button clicking you change this field value and then you invoke ListAdapter.notifyDataSetChanged so the List View updates - getView then gets called and image changes because your field value has changed.
Inside the getView() of your adapter, you grab the ImageView and set its visibility to gone:
ImageView iv = (ImageView)convertView.findViewById(R.id.image_view);
iv.setVisibility(buttonClicked ? View.GONE : View.VISIBLE);
Then when users click on the button, set buttonClicked = true, and call notifyDataSetChanged() to refresh the ListView.
My ListActivity is bound to an ArrayAdapter where I have overriden the getView() method. If I change the visibility of a widget in that method or modify the view's background color, those changes somehow get lost once that modified list item returns after being scrolled off the screen. In fact, some other view in the list is picking up the changes.
How do I get the modified view to look the same when it's redisplayed after scrolling?
Are you inflating a new view each time or making use of the convertView that is passed in?
Normally the Adapter tries to recycle views, only creating enough to provide smooth scrolling. The existing recycled views are passed in as convertView. You can either inflate and return a new view every time (expensive) or just re-setup the convertView (if it exists) based on position. If recycling you need to re-set all the view attributes, as there is no guarantee that the recycled view you get is the same one used for this position in the past.
It sounds like your bug is that you are not correctly re-setting all the attributes of the recycled view (convertView) to match the data for the current position.