我用谷歌搜索了这个,但找不到答案,所以这里......
我有一个显示一些文字和图像的ListView。底层适配器出于性能原因(根据推荐的方法)回收视图,并根据找到的on the Android Developers site's bitmap page建议使用AsynchTask加载图像。
一切都很顺利,但我有一个问题。当适配器中的视图被回收时,它仍然具有对旧图像(ImageView)的引用。如果用户滚动缓慢,则AsynchTask有足够的时间加载新图像并显示它,因此没有可见的新图像重新加载到用户。
但是,如果用户滚动得非常快,则加载图像的延迟意味着他们会在新图像替换之前看到旧图像(当View被另一个项目使用时加载)。
所以,我的问题是,我如何检测屏幕上何时不再显示视图,以便我可以删除图像?这意味着用户会看到一个空列表视图项,最终将使用适当的图像加载,这看起来会更好。
非常感谢提前。这是列表视图适配器代码。
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
ListViewHolder viewHolder;
// if this is not a recycled view then create a new view for the data...
if (view == null)
{
view = this.inflater.inflate(R.layout.target_list_view_layout, null, true);
viewHolder = new ListViewHolder();
viewHolder.manufacturer = (TextView) view.findViewById(R.id.manufacturer);
viewHolder.targetName = (TextView) view.findViewById(R.id.targetName);
viewHolder.targetThumbnail = (ImageView) view.findViewById(R.id.targetThumbnail);
view.setTag(viewHolder);
} else
{
viewHolder = (ListViewHolder) convertView.getTag();
}
TargetDescriptor targetDescriptor = this.selectedTargets.get(position);
viewHolder.manufacturer.setText(targetDescriptor.manufacturer);
viewHolder.targetName.setText(targetDescriptor.targetName);
// At this point I pass the image view reference to my background task to load the image
LoadImageViewAsynchTask loadImageTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail, targetDescriptor);
loadImageTask.execute(new Integer[]
{ 64, 64 });
return view;
}
编辑:那些使用eBay Android App的人可以看到我正在寻找的效果,如果这有帮助。
答案 0 :(得分:28)
实际上有一种更简单的方法,你可以使用一个监听器:
mListView.setRecyclerListener(new AbsListView.RecyclerListener() {
@Override
public void onMovedToScrapHeap(View view) {
}
});
答案 1 :(得分:3)
无法相信我有多愚蠢,这是一个非常容易解决的问题!
基本上在我的适配器中,如果正在重新循环视图,我只需要在重新使用视图之前使ImageView位图为空。这意味着列表视图项中的图像在加载下一个图像之前是空白的。当新图像在后台加载时,我不再有旧图像存在的问题。
我还在适配器中进行了更改,取消了绑定到视图的任何当前AsynchTask,该视图可能仍在加载不再需要的先前图像。当我在列表视图中快速滚动时,这非常普遍,通常会有两个或更多图像加载备份,因此在确定正确的图像之前图像会改变几次。
以下是新代码,并对更改进行了评论,以便您了解正在发生的事情:
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
ListViewHolder viewHolder;
// if this is not a recycled view then create a new view for the data...
if (convertView == null)
{
convertView = this.inflater.inflate(R.layout.target_list_view_layout, null, true);
viewHolder = new ListViewHolder();
viewHolder.manufacturer = (TextView) convertView.findViewById(R.id.manufacturer);
viewHolder.targetName = (TextView) convertView.findViewById(R.id.targetName);
viewHolder.targetThumbnail = (ImageView) convertView.findViewById(R.id.targetThumbnail);
convertView.setTag(viewHolder);
} else
{
viewHolder = (ListViewHolder) convertView.getTag();
// Cancel the previous attempt to load an image as this is going to be superceded by the next image
viewHolder.loadImageViewAsynchTask.cancel(true);
// Clear down the old image so when this view is displayed, the user does not see the old image before the
// new image has a chance to load in the background
viewHolder.targetThumbnail.setImageBitmap(null);
}
TargetDescriptor targetDescriptor = this.selectedTargets.get(position);
viewHolder.manufacturer.setText(targetDescriptor.manufacturer);
viewHolder.targetName.setText(targetDescriptor.targetName);
LoadImageViewAsynchTask loadImageViewAsynchTask = new LoadImageViewAsynchTask(viewHolder.targetThumbnail);
loadImageViewAsynchTask.setTargetDescriptor(targetDescriptor);
loadImageViewAsynchTask.execute(new Integer[]
{ 64, 64 });
// Keep a reference to the task so we can cancel it if the view is recycled next time round to prevent
// un-neccessary image loads that are out of date
viewHolder.loadImageViewAsynchTask = loadImageViewAsynchTask;
return convertView;
}
答案 2 :(得分:0)
如果您使用LruCache作为位图并让listview中的每个视图保留LruCache项的键,您可以更好地控制它..然后,在getView中,您首先尝试从LruCache获取位图并且仅如果不存在,请再次下载。
此外,您可以在ListView上设置setOnScrollListener(),并在用户第一次滚动时使用其visibleItemCount参数调整LruCache的大小。
答案 3 :(得分:-1)
如果您使用覆盖的getView
方法显示图像,那么您可以检查convertView。如果它为null,那么你可以发现它已经回收或者之前没有初始化。
就像这样:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewholder;
if (convertView == null) {
viewholder = new ViewHolder();
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_row, parent, false);
viewholder.tvID = (TextView) convertView.findViewById(R.id.tvID);
viewholder.tvName = (TextView) convertView.findViewById(R.id.tvName);
viewholder.tvFamily = (TextView) convertView.findViewById(R.id.tvFamily);
viewholder.ivMain = (ImageView) convertView.findViewById(R.id.ivMain);
} else {
viewholder = (ViewHolder) convertView.getTag();
}
viewholder.tvID.setText(IDs[position]);
viewholder.tvName.setText(Names[position]);
viewholder.tvFamily.setText(Familys[position]);
viewholder.ivMain.setImageResource(Images[position]);
convertView.setTag(viewholder);
return convertView;
}