尽管使用ViewHolder,LazyLoader和简单的布局,但是Stuttering Listview

时间:2014-06-07 17:34:29

标签: android listview picasso android-glide

我有一个listview,可以将服务器中的图像加载到ImageView中。 Listview实现了视图模式,并且具有非常简单的行相对布局。 对于懒惰的图像加载,我已经尝试过universalimageloader,picasso和我目前正在使用基于google排球的滑翔,这应该是非常快速的。但是:我的listview在用服务器映像替换占位符映像时仍然口吃,我只是想弄清楚原因,这是我的适配器代码:

@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;
}

当我第一次滚动它时,列表只会断断续续,所以当我的图像被检索并通过滑行粘贴到列表中时。将图像粘贴到列表视图后,滚动就像从一开始就应该是平滑的。

我做错了什么?如何有效地找出导致挂断的原因?我不是Traceview的专家,它正在制作大量数据来筛选。

感谢您的帮助!

3 个答案:

答案 0 :(得分:4)

ImageView持续请求布局可能导致口吃。见ImageView.setImageDrawable

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

我可以看到单个列表项非常复杂(5需要花费大量时间进行布局,特别是如果整个列表都放在每个图像加载上。

密钥是R.drawable.placeholder的大小。在第一次加载时,始终显示.placeholder(),因为必须从Glide磁盘缓存(即:后台I / O)读取映像。来回滚动填充内存缓存会导致立即加载(没有后台线程),并且由于这个原因,.placeholder()也没有显示→没有更改大小→没有requestLayout() < / p>

这也有助于您的ImageView具有固定大小(假设来自centerCrop()),这意味着Glide加载到此视图中的所有Bitmap将具有相同的大小并且这些Bitmap 1}} s将被回收,因此您可能在池中的Bitmap和回收的行之间具有1:1的对应关系。 此外,当现有行被回收时,ViewHolder会保留ImageView,其中已显示相同大小的图像,因此不会重新布局。

注意:Glide并非基于Volley,可以通过volley-integration使用Volley,但默认情况下它不依赖于它。虽然这个问题很老,但也许你在3.0之前说的是真的。

答案 1 :(得分:1)

看看你的logcat。也许,您遇到的口吃是由于垃圾收集器(GC)尝试为新位图重新分配堆(您应该看到 GC_FOR_ALLOC 日志)。 如果是这样的话,那就没什么可做的...... ART改进了GC的工作方式(在ART上试试)。

如果您的位图大小相同,则可以重复使用位图,以便运行时不必为新位图分配更多空间,如下所述:

https://developer.android.com/training/displaying-bitmaps/manage-memory.html#inBitmap

如果您使用的是Glide,则可以预先填充位图池以实现相同的目的。

答案 2 :(得分:0)

尝试将代码更改为:

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);
} else { // NEW
    NewsViewHolder holder = (NewsViewHolder) rowView.getTag();
}