5

I am implementing a ListFragment using a custom ArrayAdapter to populate the list. Each row item has an ImageView and three TextViews. Data is being parsed via XML and the images are being async loaded.

The problem I am having is that the ListView populates and looks good, but there is a problem when scrolling. I can fit 7 items on the screen at once. When I scroll to the 8th, it changes suddenly so the next row that should be appearing. It only does it on rows divisible by 8 (ie. rows 8, 16, 24, etc).

I'm using the ViewHolder pattern to ensure good speed with this ListView. I figure the problem lies in there somewhere, but I have searched around and it appears I am doing this pattern correctly and I have run out of things to check in order to resolve this issue. What am I doing wrong? Thanks!

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View row = convertView;
    MyViewHolder holder;
    if (row == null) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        row = inflater.inflate(R.layout.browse_result_list_item, null, false);
        holder = new MyViewHolder();
        holder.adTitle = (TextView) row.findViewById(R.id.adTitle);
        holder.region = (TextView) row.findViewById(R.id.region);
        holder.time = (TextView) row.findViewById(R.id.time);
        holder.thumbnail = (ImageView) row.findViewById(R.id.browseThumbnail);
        row.setTag(holder);
    } else {
        holder = (MyViewHolder) row.getTag();
    }
    SearchResult result = mObjects.get(position);

    holder.adTitle.setText(result.getTitle().substring(0, result.getTitle().length()-3)); // three ... at the end, remove them
    holder.region.setText(result.getRegion());
    holder.time.setText(result.getPostingTime());

    // Download the image thumbnail
    ArrayList<String> urls = result.getImageUrls();
    if (urls.size() > 0)
        download(urls.get(0), holder.thumbnail);
    else // No image for this post, put a placeholder
        holder.thumbnail.setImageResource(R.drawable.ic_action_picture);

    return row;
}

private static class MyViewHolder {
    public static TextView adTitle;
    public static TextView region;
    public static TextView time;
    public static ImageView thumbnail;
}

Edit: I found the solution thanks to @frozenkoi. Ended up being the static variables inside the ViewHolder causing problems. They are now simply public and the class is static and the issue has bee solved.

Community
  • 1
  • 1
Alex Helms
  • 93
  • 1
  • 2
  • 7
  • You don't show the code for the `download` function. Most likely when you're scrolling you have two async downloads update the same `ImageView`. You need to ensure that when the `ListView` recycles a row's view that it is only updated by the correct downloader and not for the downloader meant for the row the view was representing before. – frozenkoi Nov 03 '13 at 11:13
  • Possibly a duplicate of http://stackoverflow.com/questions/16540582/list-view-image-is-getting-changed-when-scrolling – frozenkoi Nov 03 '13 at 11:21
  • Should that sentence read "it changes suddenly **to** the next row that should be appearing." instead? Can you give a little example of the effect? 1 2 3 4 5 6 7 becomes what? – stackunderflow Nov 03 '13 at 11:24
  • More info on lazy loading images in a list or grid view: http://stackoverflow.com/questions/541966/how-do-i-do-a-lazy-load-of-images-in-listview – frozenkoi Nov 03 '13 at 11:27
  • 1
    check this if it helps http://stackoverflow.com/questions/16789676/caching-images-and-displaying. Lazy Loading – Raghunandan Nov 03 '13 at 11:28
  • I would try to get rid of the download code and try to use some resource or bitmap for all the images, if the problem disappears that the download code is to blame. – Bob Nov 03 '13 at 12:59
  • Thank you for the suggestions. I have solved the problem, thanks to @frozenkoi. Apparently in my ViewHolder class, having the variables static is what caused this problem! No more static, no more strange scroll issue in the list view. Also, I should note that during the debug, I disabled downloading images and just put a bitmap from resources in the row instead and there was no change. Thanks again guys! – Alex Helms Nov 03 '13 at 18:05

5 Answers5

3

I found the solution thanks to @frozenkoi. Ended up being the static variables inside the ViewHolder causing problems. They are now simply public and the class is static and the issue has been solved.

private static class MyViewHolder {
    public TextView adTitle;
    public TextView region;
    public TextView time;
    public ImageView thumbnail;
}
MarsAtomic
  • 10,436
  • 5
  • 35
  • 56
Alex Helms
  • 93
  • 1
  • 2
  • 7
  • I am getting `The member type ViewHolder cannot be declared static; static types can only be declared in static or top level types` – Pratik Butani May 14 '15 at 10:38
3

First of all define in getView() method into convertView=null; and comment else condition in this method and add int type = getItemViewType(position);. Also remove static keyword from each View item like Textview, ImageView in ViewHolder class.

NOTE: override getViewTypeCount() and getItemViewType() in your adapter.

if(convertView == null){
 switch (type) {
            case 0:
                  //First view[Row layout]
            break;
            case 1:
                 //Second view[Row layout]
            break;

            //another Case here....

   convertView.setTag(holder);

  //remove else part when used convertView =null;
        /*else {
        holder = (MyViewHolder) row.getTag();
    }*/

} 

@Override
    public int getItemViewType(int position) {
        // TODO Auto-generated method stub
        map = list.get(position); 
        message_type = map.get("message_type");
        if (message_type.equalsIgnoreCase("TEXT")) {
            return 0;
        } else {
            return 1;
        }

    }

    @Override
    public int getViewTypeCount() {
        // TODO Auto-generated method stub
        if (getCount() != 0)
            return getCount();
        return 2;
    }

Do it now! Done!

1

Just add these lines of code in your adapter class.

@Override
public int getViewTypeCount() {

    if (getCount() != 0)
        return getCount();

    return 1;
}

Hope it will solve your problem.

androidcodehunter
  • 21,567
  • 19
  • 47
  • 70
0

Problem comes only when viewholder variables are defined static. Otherwise listview works good with or without viewholder in scroll.

  • Welcome to stackoverflow!! Try to avoid comment in answer unless you have specific answer. Please use comment box below and Once you have sufficient reputation you will be able to comment on any post. :) – Rucha Bhatt Joshi Jun 21 '17 at 05:53
0

In my case revert back was forgotten in if else condition:

/*public class ContactAdapter extends BaseAdapter {
    public List<Contact> _data;
    private ArrayList<Contact> arraylist;
    Context _c;
    ViewHolder holder;

public ContactAdapter(List<Contact> contacts, Context context) {
    _data = contacts;
    _c = context;
    this.arraylist = new ArrayList<Contact>();
    this.arraylist.addAll(_data);
}*/
@Override
public View getView(final int i,  View convertView, final ViewGroup viewGroup) {
    View view = convertView;
    final Contact data = (Contact) _data.get(i);
    ViewHolder holder ;

    if (view == null) {
        Context context = viewGroup.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        view = inflater.inflate(R.layout.contact_list, null,false);
        holder = new ViewHolder();
        holder.title = (TextView) view.findViewById(R.id.name);
        holder.imageView = (ImageView) view.findViewById(R.id.pic);
        view.setTag(holder);
    } else {
        holder = (ViewHolder) view.getTag();
    }

    holder.title.setText(data.getName());
    // Set image if is null string
    if(!data.getThumb().equals("null")){
        try {
             Bitmap myBitmap = BitmapFactory.decodeFile(ImageStorage.getImage(data.getThumb()).getAbsolutePath());
             holder.imageView.setImageBitmap(myBitmap);
            }catch (Exception e){e.printStackTrace();
             holder.imageView.setImageResource(R.mipmap.contactimage);
          }

    }else // I had forgotten this else 
        holder.imageView.setImageResource(R.mipmap.contactimage); 


    // same action with background color
    if(data.getId()==data.getFromContact() ){
         view.setBackgroundColor(Color.RED);
     } else{
         view.setBackgroundColor(Color.WHITE); //**revert back it**
     }
        return view;
}

ViewHolder

  private  static class ViewHolder {
    ImageView imageView;
    TextView title;

}