13

I have a ListView with custom ArrayAdapter. Each of the row in this ListView has an icon and some text. These icons are downloaded in background,cached and then using a callback, substituted in their respective ImageViews. The logic to get a thumbnail from cache or download is triggered every time getView() runs.

Now, according to Romain Guy:

"there is absolutely no guarantee on the order in which getView() will be called nor how many times."

I have seen this happen, for a row of size two getView() was being called six times!

How do I change my code to avoid duplicate thumbnail-fetch-requests and also handle view recycling?

Thanks.

Charles Stewart
  • 11,661
  • 4
  • 46
  • 85
Samuh
  • 36,316
  • 26
  • 109
  • 116

7 Answers7

14

Exactly, that could happen for example when you have

android:layout_height="wrap_content"

in your ListView definition. Changing it to fill_parent/match_parent would avoid it.

jBilbo
  • 1,683
  • 13
  • 24
  • 1
    It does not completely evict the redundant call problem; but it helps measure the height of the ListView better. And if the height is measured correctly, ListView can correctly determine the number of rows currently visible which could reduce the number of redundant getView() calls. – Samuh May 28 '10 at 11:49
  • This helped me, even with and horizontal ListView, changing the width instead of the height. – vantesllar Oct 08 '14 at 17:35
  • this was the answer for me...thanks a lot for saved time – user1071762 Oct 23 '14 at 08:19
8

From api.

public abstract View   getView  (int position, View  convertView, 
                                 ViewGroup  parent)

convertView - The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.

So if getView has already been called for this specific index then convertView will be the View object that was returned from that first call.

You can do something like.

if(!(convertView instanceof ImageView)){
   convertView = new ImageView();
   //get image from whereever
} else {} // ImageView already created
Robby Pond
  • 73,164
  • 16
  • 126
  • 119
1

I m experiancing the same issue i change the layout_height of listView to match_parent resolve my issue.

Muhammad Aamir Ali
  • 20,419
  • 10
  • 66
  • 57
1

My understanding is that you need to use the ViewHolder design pattern here. Just using a returned convertView can lead to reuse of a previous view (with some other image assigned in this case).

public class ImageAdapter extends ArrayAdapter<String> {

  // Image adapter code goes here.

  private ViewHolder {
    public ImageView imageView;
    public String url;
  }

  @Override
  public View getView(final int position, View convertView, ViewGroup parent) {
    View view = null;
    ViewHolder viewHolder;
    String url = getUrl(position);
    if (convertView == null) {
      // There was no view to recycle. Create a new view.
      view = inflator.inflate(R.layout.image_layout, parent, false);
      viewHolder = new ViewHolder();
      viewHolder.imageView = (ImageView) view.findViewById(R.id.image_view);
      viewHolder.url = url;
      view.setTag(viewHolder);
    } else {
      // We got a view that can be recycled.
        view = convertView;
        viewHolder = ((ViewHolder) view.getTag());
        if (viewHolder.url.equals(url)) {
          // Nothing to do, we have the view with the correct info already.
          return view;
        }
    }

    // Do work to set your imageView which can be accessed by viewHolder.imageView

    return view;
  }
}
wislo
  • 1,110
  • 2
  • 13
  • 24
0

The better would be to create a object with Thumbnail(bitmap) and the text. And read the thumbnail if its not available in the object.

Karan
  • 12,724
  • 6
  • 40
  • 33
0

Create an array of ImageView objects in your adapter and cache them as you retrive them (whether from cache or web). For example, in getView, before you fetch the ImageView, check if it's already in your local array, if so, use it, if not fetch, once received store in your local ImageView array for future use.

Ricardo Villamil
  • 5,031
  • 2
  • 30
  • 26
0

My Fragment.xml has a ListView, the layout setting of this ListView was android:layout_height="wrap_content", and this ListView will bind to SimpleCursorAdapter later. Then I have the same issue in ViewBinder be called 3 times. The issue resolved after I change the layout_height="wrap_content" to "95p". I do consider that the "wrap_content" height cause this issue. Trying to modify your Fragment.xml and I guess the 3 times called issue will no longer exist.

Jim Lu
  • 3
  • 1
  • 4