知道ListView中的View何时离开了屏幕?

时间:2013-10-27 17:42:53

标签: android image listview asynchronous

我用谷歌搜索了这个,但找不到答案,所以这里......

我有一个显示一些文字和图像的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的人可以看到我正在寻找的效果,如果这有帮助。

4 个答案:

答案 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;
}