2009-09-11 47 views
6

这是一种非常常见的情况:在必须从Internet下载的ListView中显示图像。Android - 将图像延迟加载到ListView中的问题

现在我有一个ArrayAdapter的自定义子类,它用于ListView。在ArrayAdapter的getView()实现中,我生成了一个单独的线程来加载图像。加载完成后,它会查找合适的ImageView并使用ImageView.setImageDrawable()设置图像。所以我使用的解决方案有点类似于这个:Lazy load of images in ListView

我遇到的问题是,只要我调用ImageView上的setImageDrawable(),ListView以某种方式刷新所有当前可见行列表!这导致一种无限循环的:

  1. getView()被调用
  2. 线程派生加载图像
  3. 图像被加载; setImageDrawable()调用ImageView的
  4. ListView控件将它拾起出于某种原因,并刷新自身
  5. 对于ListView刷新,getView()被调用每个可见行,让我们回到步骤1,整个事情重复自己

因此,据我所见,解决方案中提出的“Android - 我如何在ListView中执行延迟加载的图像”(请参阅​​上面的链接)根本不起作用。它可能看起来像它,但它会运行得非常慢,因为在后台,它会不断重新加载当前可见的行。

有没有人遇到过和/或有解决方案吗?

回答

2

在链接的解决方案中,fetchDrawableOnThread()只应在视图尚未具有正确的可绘制项的情况下才能调用。

如果getDrawable()返回null,则视图不具有可绘制的。

如果您重复使用插槽,您认为您需要进一步管理状态。例如,如果你的视图有一个存储URL的成员变量,并且有一个布尔值来表示它是否被加载,那么很容易知道是否调用fetchDrawableOnThread()

我推测可绘制的toString()详细说明了图像加载的路径。 (如果没有,你可以将返回的drawable子类化)。在这种情况下,您可以避免上面概述的布尔值,只是做一个比较来确定它是否可以右键绘制或者是否获取替换。

此外,您的getView()在可见行上应确保那些不再可见的被卸载,以防止内存耗尽。一个技巧就是将不再可见的图像移动到软引用(因此当需要内存时它们被卸载),作为另一张原始线程上的海报。

+1

是的我正在使用地图来缓存图像。但是,这并不重要,因为我最终仍然调用setImageDrawable(),它再次触发刷新。如果我能以某种方式禁用refesh它会解决我的问题。 我不使用SoftReferences(但我会),但这只是一个内存优化,这不能解决无限循环 – 2009-09-11 09:00:53

+0

好点我误解了你的意思是'线程被派生到*加载*图像' 。我会重写我的回答 – Will 2009-09-11 09:03:58

+0

感谢您的快速回复:)当您不重复使用视图来显示像我一样的行时,您现在描述的内容确实解决了问题。重用行我的意思是使用getView()给出的“convertView”参数。在重新使用行视图时,你是否知道这个解决方案? (因为在这种情况下,你必须每次都调用setImageDrawable()) – 2009-09-11 09:25:07

3

我用下面的链接代码:another stackoverflow question

我为了解决回收视图的疑难问题。一些小的修改设定图像的URL适配器标签的ImageView的。以下代码包含解决回收问题的解决方案:

public void fetchDrawableOnThread(final String urlString, final ImageView imageView,Drawable drw) { 

    imageView.setImageDrawable(drw);//drw is default image 
    if (drawableMap.containsKey(urlString)) { 
     if(imageView.getTag().toString().equals(urlString)) 
     { 
      imageView.setImageBitmap(drawableMap.get(urlString)); 
      imageView.invalidate(); 
      return; 
     } 

    } 

    final Handler handler = new Handler() { 
     @Override 
     public void handleMessage(Message message) { 
      BitmapWrapper wrapper = (BitmapWrapper)message.obj; 
      if(wrapper.imageurl.equals(imageView.getTag().toString())) 
      { 
       imageView.setImageBitmap((Bitmap)wrapper.bitmap); 
       imageView.invalidate(); 
      } 

     } 
    }; 

    Thread thread = new Thread() { 
     @Override 
     public void run() { 
      //TODO : set imageView to a "pending" image 

      Bitmap drawable = fetchDrawable(urlString); 
      BitmapWrapper wrapper = new BitmapWrapper(); 
      wrapper.bitmap = drawable; 
      wrapper.imageurl = urlString; 
      Message message = handler.obtainMessage(1, wrapper); 
      handler.sendMessage(message); 
     } 
    }; 
    thread.start(); 
} 


    public class BitmapWrapper 
{ 
    public Bitmap bitmap; 
    public String imageurl; 
} 
3

我有同样的问题。

经过近2天重调试/优化,并试图弄清楚,为什么在连续使用setImageBitmap()当我getView()被要求的所有意见一遍又一遍,我想出了一个肮脏的解决方案:

1)扩展的自定义ImageView,你在你的名单

2)在此ImageView使用的所有图像覆盖的方法

@Override 
public void requestLayout() 
{ 
    return; 
} 

3)脏,但对我来说,它的作品

4)利润;)

+1

当列表中的图像大小相同并且你的图像视图也是固定大小(不包含内容)时,可以使用它。这帮了我,谢谢:) – Mark 2013-02-15 09:41:47