ImageView:如果ImageView在ScrollView中不可见,则自动回收位图

8

我已经研究了一段时间的ImageView源代码,但是还没有找到可以做到这一点的方法。

问题:ScrollView中有30个400x800像素的图片(图片数量可变)。由于它们恰好适合屏幕大小,因此每个图片需要1.3 MB的RAM。

我的需求:

  • 能够加载/卸载当前在ScrollView中可见的ImageView的位图。
  • 如果用户滚动并且位图不再可见(在距离阈值内),则应释放位图,以便同一ScrollView中的其他位图可以使用该内存。我正在做缩小和所有操作,所以不要担心。
  • 如果可能,只扩展ImageView来实现此目标,而不是与ScrollView混淆。

总结:我可以通过巧妙的技巧使图像仅在ImageView可见后加载,但是我不知道何时卸载它们。

注:由于其他可用性原因,我无法使用ListView来完成此操作。

3个回答

5

当您想要回收位图时,请调用此方法。

public static void recycleImagesFromView(View view) {
            if(view instanceof ImageView)
            {
                Drawable drawable = ((ImageView)view).getDrawable();
                if(drawable instanceof BitmapDrawable)
                {
                    BitmapDrawable bitmapDrawable = (BitmapDrawable)drawable;
                    bitmapDrawable.getBitmap().recycle();
                }
            }

            if (view instanceof ViewGroup) {
                for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
                    recycleImagesFromView(((ViewGroup) view).getChildAt(i));
                }
            }
        }

谢谢您的普遍回答。但是您确定这种方式是高效的,不会造成相当的延迟吗? - Hamzeh Soboh
我添加了一个缺失的空值检查,因为getBitmap()可能返回null;请参见BitmapDrawable.java:“返回此可绘制对象用于渲染的位图。可能为空。” - Oliver Hausler

1

请查看有关"如何绘制位图"的Android文档,以及名为“bitmap fun”的代码示例。

如果您使用ListView或GridView,则ImageView对象将被回收,并且实际位图将被释放以供GC清理。

您还希望将原始图像调整为屏幕大小并将其缓存到磁盘和/或RAM中。这将为您节省大量空间,实际大小应该降至几百KB。您还可以尝试以显示器分辨率的一半显示图像。结果应该仍然很好,同时使用更少的RAM。


是的,对于ListView来说,这将是“默认”行为。但对于ScrollView来说,我最终找到了解决方法,但过程并不美观。 - dmon
@dmon,你能分享你解决方案的任何部分吗?你是否100%确定ListView会自动处理失去焦点的视图?这适用于所有Android版本还是只有2.2+版本才支持? - nikib3ro
我编写代码的最低API版本是7级。我可以在这里发布更多信息。现在有点忙。但也许本周末可以。 - philipp

0
你真正想要使用的是 ArrayAdapter。如果像你的备注所示,这不可能(虽然我不知道为什么),请查看数组适配器的源代码,并重写一个以满足你自己的需求。

我已经使用了一个数组适配器来生成包含这些ImageView的组件。仅仅使用ArrayAdapter本身并没有什么聪明的功能。 - dmon
数组适配器在视图移出视野时会释放所有视图的引用(或多或少)。这将自动强制回收您的图像。您不需要显式调用图像的回收。但是,您需要确保没有对位图的强引用,以便它们可以被释放。 - Paul Nikonowicz
我一直在做的就是将图像存储在文件系统中,在getView中,从文件系统绑定位图到视图。 - Paul Nikonowicz
再次强调,这并不是适配器的问题,而是持有适配器的任何东西,这种情况下可能是一个 ListView。适配器本身除了返回一个新视图或重用提供的视图之外什么也不做。 - dmon
我明白你的意思。既然你不想使用ListView,那么你也不需要适配器。 - Paul Nikonowicz
查看ListView的源代码,以了解何时调用适配器上的getView方法。这应该会告诉你如何发现焦点项。 - Paul Nikonowicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接