【问题标题】:Stuttering Listview despite ViewHolder, LazyLoader and simple Layout尽管有 ViewHolder、LazyLoader 和简单的布局,但 Listview 口吃
【发布时间】:2014-06-07 17:34:28
【问题描述】:

我有一个列表视图,可以将服务器中的图像加载到 ImageView 中。 Listview 实现了 viewholder 模式,并有一个非常简单的行相对布局。 对于延迟图像加载,我尝试了universalimageloader,picasso,目前正在使用基于google volley的glide,它应该非常快。但是:我的列表视图在用服务器图像替换占位符图像时仍然口吃,我只是不知道为什么,这是我的适配器代码:

@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 方面的专家,它会产生大量数据进行筛选。

感谢您的帮助!

【问题讨论】:

  • “我不是 Traceview 方面的专家”——嗯,这是你最好的选择。如果您还没有这样做,请将StrictMode 设置为最大程度地大喊大叫,希望您的问题可能与主应用程序线程上的磁盘或网络 I/O 相关。

标签: android listview picasso android-glide


【解决方案1】:

卡顿可能是由ImageView 不断请求布局引起的。见ImageView.setImageDrawable:

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

我可以看到单个列表项非常复杂(5 需要花费大量时间来布置,尤其是在每次加载图像时都布置整个列表时。

关键是R.drawable.placeholder的大小。在第一次加载时,总是显示.placeholder(),因为必须从 Glide 磁盘缓存中读取图像(即:后台 I/O)。来回滚动会填充内存缓存,这会导致立即加载(无后台线程),并且 .placeholder() 也因此未显示 → 没有大小变化 → 没有 requestLayout()

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

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

【讨论】:

    【解决方案2】:

    看看你的 logcat。也许,您遇到的卡顿是由于垃圾收集器 (GC) 试图为新的位图重新分配堆(您应该看到 GC_FOR_ALLOC 日志)。 如果是这种情况,那就没什么可做的了……ART 极大地改进了 GC 的工作方式(在 ART 上尝试一下)。

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

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

    如果你使用 Glide,你可以预先填充位图池来达到同样的效果。

    【讨论】:

      【解决方案3】:

      尝试将您的代码更改为:

      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();
      }
      

      【讨论】:

      • @micnubinub 他的充气机只会被调用一次,只是第一次
      • 在发布问题后我就这样做了,不幸的是它没有帮助。我认为问题不在于行膨胀,因为在加载所有图像后列表会平滑滚动。
      • @Doge 你是对的,方法调用和强制转换不会真正导致口吃。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多