我有一个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的专家,它正在制作大量数据来筛选。
感谢您的帮助!
答案 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();
}