Search code examples
androidlistviewpicassoandroid-glide

Stuttering Listview despite ViewHolder, LazyLoader and simple Layout


I have a listview that loads images from a server into an ImageView. The Listview implements the viewholder pattern and has a very simple relative-layout for the rows. For lazy image loading, I have tried universalimageloader, picasso and am currently using glide, which is based on google volley, which should be blazing fast. However: My listview still stutters while replacing the placeholder image with the servers image and I just cant figure out why, here is my adapter-code:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View rowView = convertView;
        News newsItem = getItem(position);

        if (rowView == null) {
            LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            rowView = inflater.inflate(mResource, null);
            // Create a viewHolder enabled row
            NewsViewHolder viewHolder = new NewsViewHolder();
            viewHolder.newsTitle = (TextView) rowView.findViewById(R.id.news_title);
            viewHolder.newsSubTitle = (TextView) rowView.findViewById(R.id.news_sub_title);
            viewHolder.newsDescription = (TextView) rowView.findViewById(R.id.news_desc);
            viewHolder.newsDate = (TextView) rowView.findViewById(R.id.news_date);
            viewHolder.newsThumb = (ImageView) rowView.findViewById(R.id.news_thumb);
            rowView.setTag(viewHolder);
        }
        NewsViewHolder holder = (NewsViewHolder) rowView.getTag();

        // Fill in fields of the current row of the NewsListView
        holder.newsTitle.setText(newsItem.title);
        holder.newsSubTitle.setText(newsItem.subTitle);
        holder.newsDescription.setText(newsItem.desciption);
        holder.newsDate.setText(SimpleDateFormat.getDateInstance().format(new Date(Long.parseLong(newsItem.date) * 1000)));

        Glide.load(newsItem.imageThumb)
                .centerCrop()
                .placeholder(R.drawable.placeholder)
                .into(holder.newsThumb);

        return rowView;
    }
}

/*
 * ViewHolder class for NewsListView
 */
public static class NewsViewHolder {
    public TextView newsTitle;
    public TextView newsSubTitle;
    public TextView newsDescription;
    public TextView newsDate;
    public ImageView newsThumb;
}

The list only stutters while i am scrolling it for the first time, so when my images are retrieved and pasted into the list by glide. After the images are pasted into the listview scrolling is buttery smooth as it should be from the start.

What did I do wrong? How can I efficiently find out what is causing the hangup? I am not an expert in Traceview and it is producing a lot of data to sift through.

Thanks for any help!


Solution

  • Stuttering can be caused by ImageView continuously requesting layout. See ImageView.setImageDrawable:

    if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
        requestLayout();
    }
    

    I can see that a single list item is really complex (5 which takes a lot of time to lay out, especially if the whole list is laid out on every image load.

    The key is the size of the R.drawable.placeholder. On the first load the .placeholder() is always shown, because the image must be read from the Glide disk cache (that is: background I/O). Scrolling back and forth fills the memory cache which results in immediate loads (no background thread) and also the .placeholder() is not shown for this very reason → no size change → no requestLayout().

    It also helps that your ImageView has a fixed size (assuming from centerCrop()) which means that all Bitmap loaded by Glide into this view will be the same size and these Bitmaps will be recycled, so you'll probably have a 1:1 correspondence between Bitmaps in the pool and your recycled rows. Also when an existing row is recycled the ViewHolder holds an ImageView that has a same sized image already displayed in it so there will be no re-layout.

    Note: Glide is not based on Volley, there's a possiblity of using Volley via volley-integration, but by default it has no dependency on it. Though this question is quite old, maybe what you stated was true before 3.0.