custom listview adapter getView method being called multiple times, and in no coherent order

JavaAndroidListview

Java Problem Overview


I have a custom list adapter:

class ResultsListAdapter extends ArrayAdapter<RecordItem> {

in the overridden 'getView' method I do a print to check what position is and whether it is a convertView or not:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        System.out.println("getView " + position + " " + convertView);

The output of this (when the list is first displayed, no user input as yet)

04-11 16:24:05.860: INFO/System.out(681): getView 0 null  
04-11 16:24:29.020: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43d415d8  
04-11 16:25:48.070: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.110: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43d415d8  
04-11 16:25:49.710: INFO/System.out(681): getView 0 android.widget.RelativeLayout@43d415d8  
04-11 16:25:50.251: INFO/System.out(681): getView 1 null  
04-11 16:26:01.300: INFO/System.out(681): getView 2 null  
04-11 16:26:02.020: INFO/System.out(681): getView 3 null  
04-11 16:28:28.091: INFO/System.out(681): getView 0 null  
04-11 16:37:46.180: INFO/System.out(681): getView 1 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.091: INFO/System.out(681): getView 2 android.widget.RelativeLayout@43cff8f0  
04-11 16:37:47.730: INFO/System.out(681): getView 3 android.widget.RelativeLayout@43cff8f0  

AFAIK, though I couldn't find it stated explicitly, getView() is only called for visible rows. Since my app starts with four visible rows at least the position numbers cycling from 0-3 makes sense. But the rest is a mess:

  • Why is getview called for each row three times?
  • Where are these convertViews coming from when I haven't scrolled yet?

I did a bit of reseach, and without getting a good answer, I did notice that people were associating this issue with layout issues. So in case, here's the layout that contains the list:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_height="fill_parent"
	android:layout_width="fill_parent" 
	android:orientation="vertical" >
	
	<TextView android:id="@+id/pageDetails"
		android:layout_width="fill_parent" 
		android:layout_height="wrap_content" />
		
	<ListView android:id="@+id/list"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content" 
		android:drawSelectorOnTop="false" />

</LinearLayout>

and the layout of each individual row:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="108dp"    
android:padding="4dp">

<ImageView
    android:id="@+id/thumb"        
    android:layout_width="120dp"
    android:layout_height="fill_parent"        
    android:layout_alignParentTop="true"
    android:layout_alignParentBottom="true"
    android:layout_alignParentLeft="true"
    android:layout_marginRight="8dp"        
    android:src="@drawable/loading" />

<TextView  
    android:id="@+id/price"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentBottom="true"       
    android:singleLine="true" />
    
<TextView  
    android:id="@+id/date"
    android:layout_width="wrap_content"
    android:layout_height="18dp"         
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true" 
    android:paddingRight="4dp"       
    android:singleLine="true" />

<TextView
	android:id="@+id/title"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textSize="17dp" 
    android:layout_toRightOf="@id/thumb"
    android:layout_alignParentRight="true"
    android:layout_alignParentTop="true"
    android:paddingRight="4dp"   
    android:layout_alignWithParentIfMissing="true"
    android:gravity="center" />
    
</RelativeLayout>

Thank you for your time

Java Solutions


Solution 1 - Java

This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times. In your particular case you are doing the worst thing possible with a ListView by giving it a height=wrap_content. This forces ListView to measure a few children out of the adapter at layout time, to know how big it should be. This is what provides ListView with the convertViews you see passed to getView() even before you scroll.

Solution 2 - Java

Try with match_parent on the layout_height property of the list view. It will prevent getView() to be called so often.

Solution 3 - Java

I got rid of this issue when I changed both layout_width and layout_height to match_parent (changing only layout_height didn't help).


Helpful note watch out if you have nested items. You've got to change the "highest" one to match_parent. Hope it helps someone.

Solution 4 - Java

I am not able to answer your "Why" question but i definitely have a solution to the problem of the irritating "ListView items repeating" problem(if you have items in ur collection which are more than the screen height).

As many people above have mentioned, keep the android:layout_height property of the ListVew tag as fill_parent.

And about the getView() function, the solution is to use a static class called ViewHolder. Check out this example. It successfully does the task of adding all the items in ur Array or ArrayCollection.

Hope this helps friends!!

Best Regards, Siddhant

Solution 5 - Java

Ques: Why Adapter calls getView() manytimes? Ans: As Listview renders on scrolling is refreshes it's view with next upcoming views, for which adapter needs to get views by calling getView().

Ques: Why it's calls lesser if listview width and height set to fill_parent? Ans: Because as inflator has the fixed size for the screen area for list it calculates once for rendering the views onto the screen.

Hope it will resolve your query.

Solution 6 - Java

I was having the same problem with the dropdown in an AutoCompleteTextView. I was fighting with the problem for two days until I arrive here and you show me the solution.

If I write dropDownHeight="match_parent" the problem is fixed. Now the problem is related to UI (when you have one item the dropdown is too large) but the problem of multiple calls (much more important) is fixed.

Thank you!!

Solution 7 - Java

For all of you who still (After setting the height of the ListView to match_parent) are stuck (like I was):

You also have to set the height of the parent layout to match_parent.

See example below. The LinearLayout is the parent here:

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
   
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/something" />
    
        <ListView
            android:id="@+id/friendsList"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

Solution 8 - Java

"Why is getview called for each row three times?" Because getView is called when you scroll on listview and to say better then that it is called when the position of an view of your list is changed!

Solution 9 - Java

I am having the same issue. If I have height set to fill_parent, then I get "usually" 2 calls per row. But, if I set height of my ListView to exact value, let's say to 300dp, then I get exactly one GetView call per row.

So, it seems to me that the only way is to first determine height of the screen, then programmatically set height of listvilew to that value. I don't like it. I hope there is a better way.

Solution 10 - Java

This maybe coming late but if you are using layout_weight remember to always set layout_width="0dp"

Solution 11 - Java

i made this solution, maybe is not the best one, but does the work...

//initialize control string
private String control_input = " ";

then =

@Override
public View getView(int position, View convertView, ViewGroup parent) {

    View gridview = convertView;

    // change input_array for the array you use
    if (!control_input.equals(input_array[position])) {
        control_input = input_array[position];
        
        //do your magic
        
    } 
    
    return gridview;
}

hope it helps!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionedzillionView Question on Stackoverflow
Solution 1 - JavaRomain GuyView Answer on Stackoverflow
Solution 2 - JavaFred TView Answer on Stackoverflow
Solution 3 - JavaEugene ChumakView Answer on Stackoverflow
Solution 4 - JavaSiddhantView Answer on Stackoverflow
Solution 5 - Javajitain sharmaView Answer on Stackoverflow
Solution 6 - JavaPau Arlandis MartinezView Answer on Stackoverflow
Solution 7 - JavaB001ᛦView Answer on Stackoverflow
Solution 8 - JavaUngureanu LiviuView Answer on Stackoverflow
Solution 9 - JavabobetkoView Answer on Stackoverflow
Solution 10 - JavaKuti GbolahanView Answer on Stackoverflow
Solution 11 - JavayagoView Answer on Stackoverflow