【问题标题】:Lazy-loading images in ListView on Android在 Android 上的 ListView 中延迟加载图像
【发布时间】:2010-09-22 06:36:24
【问题描述】:

我在ListView 中实现了延迟加载图像。 我使用AsyncTask 从互联网上下载图像并将其绑定到 UIThread 中的 ImageView。

除了当我快速滚动ListView 时,它的工作原理,下载的图像有时会绑定到列表中的错误项目。

我想问题出在BaseAdapterconvertView 的重用。 有什么解决办法吗?

非常感谢。

编辑: 我将答案发布如下:

public void setBitmap(int position, Bitmap image) {
    View itemView = mListView.getChildAt(position - mListView.getFirstVisiblePosition());
    if (itemView != null) {
        ImageView itemImageView = (ImageView) itemView.findViewById(R.id.item_imageview);
        itemImageView.setImageBitmap(image);
    }
}

【问题讨论】:

  • 我在这里尝试了这个例子android-developers.blogspot.com/2010/07/… 它可以工作,但仍然有机会绑定错误的图像。比较任务实例的技巧似乎与 Janusz 的答案相同。但它使用弱引用来保存 ImageView 的实例。需要深入挖掘。有什么想法吗?

标签: android android-listview lazy-loading


【解决方案1】:

在 ListView 中延迟加载图像时会出现两个问题。

  1. 在加载新图像之前,仍会显示旧图像。这很简单,只需将 ImageView 设置为图像正在加载视图或在开始图像下载之前将其设置为不可见。
  2. 第二个问题更难解决。想象一下,您正在快速浏览您的列表。现在,您的视图可能会在旧的 AsyncTask 完成加载图像之前被回收。您现在在 onPostExecute 方法中有两个正在运行的任务,它们会将图像设置为图像视图。现在会在短时间内显示错误的图像,直到第二个任务完成,或者由于某些与网络相关的原因,它们没有按照开始的顺序完成,并且您有错误的图像覆盖了正确的图像,所以更糟糕的是。要解决这个问题,您必须检查任务完成后应该显示什么图像。在View 类中,有两种方法可以处理类似这样的事情:

    setTaggetTag 您可以将任何对象绑定到您想到的图像视图。在大多数情况下,我在开始任务之前使用 setTag 将图像的 URL 作为字符串绑定到图像视图。现在我可以在任务完成后将 getTag 转换为字符串,并将应该显示的 URL 与我下载的 URL 进行比较,并仅在必要时设置图像。

【讨论】:

  • 谢谢!您描述的第一个问题对我来说似乎不是解决方案。因为ImageView 在滚动变化很快时会被绑定多次。所以将 ImageView 设置为加载图像或不可见是行不通的。
  • getTag 方法似乎不会立即绑定ImageView,因为滚动后标签已更改。它仅在缓慢滚动并返回后才更新。是否有任何步骤我做错了?
  • 我不明白你的意思 getTag 不会立即绑定 ImageView 所以我不知道你是否做错了什么。
  • 快速滚动并停止某处后,ImageView 似乎没有被绑定。我需要将其稍微移出屏幕并返回以使其更新。在我尝试了这里android-developers.blogspot.com/2010/07/… 的解决方案后,我认为保留ImageView 的强引用不是一个好主意,因为它是为由ListView 发布而设计的。无论如何,感谢您的帮助。
  • 使用 setTag 解决方案仍然可以释放 imageview。来自后台任务的引用将与 asyncTask 一起不复存在,然后任务和 imageview 将被释放。您不会永远持有引用,并允许大量 ImageViews 建立和泄漏内存。它只是在任务运行期间使用的内存多一点。
【解决方案2】:

在您的适配器中创建一个名为void setBitmap(Bitmap bitmap, int position) 或类似的函数。当有新的位图可用时,让您的 AsyncTask 调用此方法。然后,此方法可能会在 UI 线程本身上调用 notifyDataSetChanged() 以确保刷新视图。在适配器中保存对视图的引用(即使将它们保存在 AsyncTask 中)是危险的!

【讨论】:

  • 这不是shiami的问题。他正在重用具有图像集的视图,并且由于在异步任务中设置了正确的图像,因此在加载新图像之前会显示旧图像等。
  • 是的。我曾经参与过一个恰好发生这种行为的项目,而使用 setTag() 或 getTag() 只是一个丑陋的解决方法。解决方案是直接在getView()方法中设置位图,该方法本身由notifyDataSetChanged()触发。当然,这会导致所有可见视图的刷新,这可能会导致明显的卡顿。要仅刷新一个视图,此解决方案可能会更好:stackoverflow.com/questions/3724874/…
  • 感谢您的提示。但是我在哪里可以获得正确的视图实例来根据给定的位置绑定我下载的图像?
  • 我尝试了解决方案stackoverflow.com/questions/3724874/…,但是当滚动变化很快时,它会导致NullPointerException。似乎 getFirstVisiblePosition() 已更改,无法获得正确的位置。
  • 当然,如果视图暂时不可见,stackoverflow.com/questions/3724874/… 上的解决方案可能会返回 null - 所以你必须检查一下。我已经在那里添加了一条评论。
猜你喜欢
  • 2010-10-07
  • 2011-02-24
  • 1970-01-01
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
  • 2014-08-27
相关资源
最近更新 更多