169

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

edzillion
  • 3,592
  • 5
  • 31
  • 50

12 Answers12

277

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.

Jainendra
  • 24,713
  • 30
  • 122
  • 169
Romain Guy
  • 97,993
  • 18
  • 219
  • 200
  • "there is absolutely no guarantee on the order in which getView() will be called nor how many times" << Why? Please explain. – emeraldhieu Jun 29 '11 at 16:27
  • Because it's an implementation detail of the framework and we want to be able to change it if needed. – Romain Guy Jun 29 '11 at 16:43
  • 10
    I don't find this a good reason. That's fine if you want to be able to change, but currently it's not answering why it's doing this behavior? – cdpnet Aug 12 '11 at 23:09
  • 47
    @cdpnet He did say why it was doing it, your `ListView` has a height of `wrap_content`, so it is not sure of it's height therefore it lays some children out as a kind of tester to see what will fit in. Change your `ListView` to `fill_parent` then check your logs the calls should be dramatically reduced. – Blundell Jul 17 '12 at 17:27
  • I want to add any item at the end of list. then how can i do this? – Amit Thaper Aug 07 '12 at 09:09
  • 4
    The behavior between 2.3 and 4.x era ListView has changed significantly. The take away is, as ListView is designed, your cells/list_items need to be pure views and they need to be optimized for drawing quickly. According to the Google I/O talk on ListView, where Romain presented, getView might be called just to optimize view rendering and discard the result. While in my opinion this isn't as developer friendly as iOS UITableView, it is how it is. – Cameron Lowell Palmer Oct 18 '12 at 08:03
  • 4
    Thank you so much! I didn't understand why the getView() is called so often. I swithced wrap_content to fill_parent and now my app is fast again :) – Julia Hexen Nov 19 '12 at 16:18
  • 29
    There should be a warning when setting ListViews to layout_height=wrap_content because it causes so many performance problems. – nathanielwolf Dec 21 '12 at 19:08
  • -1, for failing to guarantee the order in which `getView()` will be called nor how many times. :-) – Jorge Gil Jul 02 '13 at 21:25
  • @RomainGuy Thank you so much ...i don't believe that problem was in wrap content.....Thank you again – Swap-IOS-Android Aug 22 '13 at 09:26
  • 4
    My list views are all set to match_parent, yet it still happens :( – Danyal Aytekin Feb 18 '14 at 18:05
  • 1
    @RomainGuy This is a problem if you are starting an animation on the item view in the `getView()` method. It is not triggered (or it appears as not triggered) when the `getView()` method is called more than once. How do we fix this? There must be a way. In Google+ for example, an animation is triggered on any item when scrolling down the posts. I'm specifically talking about a `GridView`, where setting the height to `match_parent` doesn't seem to diminish the number of times `getView()` is called on the first item. – doplumi Mar 15 '14 at 21:47
  • @RomainGuy, if you see this, what is your feeling on **whether one should use fill_parent or match_parent**? I feel match_parent is best, but I'm new to android. – Fattie Jun 01 '14 at 07:35
  • Can you take a look at similar question http://stackoverflow.com/questions/27377046? Maybe you could explain the observations. – Gishu Dec 09 '14 at 18:33
  • @RomainGuy I'm still have the same problem although it only calls twice now rather than 5/6. I'm creating a `Listview` dynamically like this: `RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT); listview.setLayoutParams(rlp); listview.setAdapter(customerAdpt);` What am I doing wrong? Thanks. – MosesA May 08 '15 at 23:39
  • I've been stuck with a performance hit for ages - wrap_layout on a list view, who would have thought. Thank you very much :) – Matthew Cawley Sep 30 '15 at 13:25
56

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

abarisone
  • 3,707
  • 11
  • 35
  • 54
Fred T
  • 569
  • 4
  • 3
  • 4
    Note that Fred T specifies the layout_height of the LIST VIEW... not the row, which is what I tried over and over – mblackwell8 Apr 12 '13 at 01:57
  • 8
    Note that `fill_parent` should be replaced with `match_parent` for API level 8 and higher. – Jason Axelson Oct 11 '13 at 00:35
  • I think getView() method will call multiple time, by setting width and height as match_parent for the ListView only solve the performance problem. – Cuong Vo Nov 30 '18 at 10:53
45

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.

Fattie
  • 27,874
  • 70
  • 431
  • 719
Eugene Chumak
  • 3,272
  • 7
  • 34
  • 52
  • one word buddy "awesome" but dont know why wrap_content is creating problem. This solved my issue. – Android Killer Oct 21 '13 at 10:07
  • @AndroidKiller when you use `wrap_content`, then `ListView` don't know how much list item are there to become List's content, thats why `ListView` tries to create as many rows as it thinks are suitable to display, and when you use `fill_parent` or `match_parent`, `ListView` thinks, alright, my height is `x` and I need `n` number of rows to show. – Adil Soomro Mar 06 '14 at 12:02
  • Thank you so much ! With this behaviour images were randomly changing because of multiple calls... Now this is ok since the beginning. – Chostakovitch May 28 '15 at 20:10
7

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

Siddhant
  • 1,074
  • 12
  • 22
  • Be cautious about using ViewHolder pattern in early version of android (pre ICS) because it can lead you to MemoryLeak exception. feel free to use it in ICS+ versions. – Vahid Ghadiri Aug 25 '15 at 22:50
  • @Siddhant thanks I spend a day tto figure out reason of repeating images in my `GridView` and changing `android:layout_height` and `android:layout_width` doesn't work for me. But using `ViewHolder`fixed repeating images for me I went through this tutorial http://android-vogue.blogspot.com/2011/06/custom-gridview-in-android-with.html – Nikolay Podolnyy Jan 04 '19 at 06:55
4

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.

jitain sharma
  • 584
  • 5
  • 19
4

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!!

3

"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!

Ungureanu Liviu
  • 4,034
  • 4
  • 37
  • 42
  • ok, but the view hasn't changed. there are four row views on screen when the activity starts, and without scrolling or any user input at all I get three getView calls for each row... – edzillion Apr 12 '10 at 13:14
  • This answer is wrong. The correct answer is that using ** wrap_content** causes a huge number of calls to getView. – Fattie Jun 01 '14 at 07:32
3

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>
B001ᛦ
  • 2,036
  • 6
  • 23
  • 31
2

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.

bobetko
  • 5,019
  • 14
  • 58
  • 85
  • 1
    Thanks for that. It's funny - this question has lain dormant for months now only to have a few comments appear over the last few weeks. I am no longer doing this project but since I have some time I am going to give this a try and then get back here to confirm. thanks again. – edzillion Jan 13 '11 at 15:12
  • same here, I get two calls per row. Rows 0,1,2,3 and then 100ms later another 0,1,2,3 - which is a pain in the arse when collecting statistics about what rows are seen – Someone Somewhere Oct 28 '14 at 22:51
1

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

Kuti Gbolahan
  • 2,379
  • 1
  • 9
  • 17
0

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!

yago
  • 59
  • 4
0

I had a similar problem, where the getView gets called three times on index 0 (i = 0) before it moves on. In my case I wanted to calculate the total price of the items, but with this problem the first item gets counted x3. To fix this I created an int indexFix = 0; and increment it when i is changed

    if (indexFix == i)
    {
        Total += itemTotal;
        indexFix++;
    }

I tried the above solutions but none seems to work in my case. If you can't fix it just control what to do in the getView

Yugerten
  • 244
  • 3
  • 13