12

Android 上的默认 Gallery 小部件不会回收视图 - 每次调用新位置的视图时,小部件总是调用设置为 nullgetView的适配器的方法。convertView

当您向后和向前滚动时,最终会创建大量视图,Gallery 存储它们的回收器组件似乎没有足够快地回收它们,从而导致 OOM 情况。

您可以使用一些大型图像作为您的画廊项目轻松测试这一点,但最终只有一个 TextView 会导致它。在适配器的方法中添加一个带有计数器的日志语句,getView以查看创建了多少新视图。

是否存在类似于画廊但也实现视图回收的第三方小部件?

4

5 回答 5

24

最后,我的解决方案是采用@CommonsWare 修改图库源代码的建议。这也是复制以下文件所必需的:

  • AdapterView
  • AbsSpinner

但这些都很简单。

之后,我修改了代码以执行以下操作:

RecycleBin( AbsSpinner)

  • 将物品一个个放入回收器中,而不是按位置放置
  • 无论请求的位置如何,都从回收器底部检索对象
  • 现有的实现假设适配器中的每个不同位置都会产生一个独特的视图。仅当您的图库仅包含一种类型的项目时,上述更改才有效,否则您需要根据项目类型和所需类型的数量添加某种键

Gallery

  • 使用反射(ugh)修改私有mGroupFlags变量ViewGroup以允许子重新排序 - 我还设置了一个布尔值,指示字段访问是否成功,我在使用组件之前进行了测试。
  • 删除了所有来电mRecycler.clear()
  • 画廊在滚动时必须显示的项目数量会发生变化,并且现有实现将在 (a) setSelection 被调用 (b) 发生运动滚动时清除回收器

通过这些修改,我newView在适配器中的方法中的计数器达到了... 7。

这是代码(位于http://en.wikipedia.org/wiki/WTFPL下的公共域 2013/08/07 中)

于 2011-05-04T11:54:47.680 回答
1

实际上有一个替代方案,虽然我没有亲自测试过:

https://github.com/falnatsheh/EcoGallery

于 2013-11-26T13:07:12.073 回答
0

我使用了来自http://code.google.com/p/android/issues/detail?id=3376#c19的补丁

于 2012-05-03T19:32:12.503 回答
0

派对迟到了,但我已经修改了 EcoGallery 来做更多的事情(并避免一些崩溃)。

我把它叫做TimelineGallery,它和Gallery 一样垃圾,但它可以平滑滚动,并且在异步加载图像时不会做奇怪的事情。

为了演示它,该示例使用 Picasso 和 PullToRefresh。

原始代码,版权等属于谷歌,所以责怪他们制作了如此糟糕的小部件。

最后说明:我不建议使用画廊,它很旧、有问题、很老套,而且可能永远不会被维护。问题不在于修复它的错误,问题在于画廊的整个架构是错误的,因此,如果不引入更多黑客,就不可能修复它。

谷歌意识到了这一点并弃用了它。使用 ViewPager 或 Horizo​​ntalScrollList 并处理各自的限制。

If you still want to go ahead and use this "gallery", feel free, it works, but it may crash your app and may frustrate you.

于 2014-01-13T23:25:29.563 回答
-1

OutOfMemory 问题的另一个更快的解决方法是尝试/捕获解码图像的代码,如果抛出 OutOfMemory 异常,则尝试再次以较小的分辨率对其进行解码。

像这样的东西:

private static Bitmap decodeFile(File f, int size, int suggestedScale) {

    int scale = 1;
    Bitmap bmp = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;

        if(suggestedScale > 0)
            scale = suggestedScale;
        else {
            if (width_tmp >= height_tmp) {
                scale = Math.round((float)(width_tmp) / size);
            } else {
                scale = Math.round((float)(height_tmp) / size);
            }
        }

        if(scale < 2)
            return BitmapFactory.decodeFile(f.getPath()); 

        Debug.i(TAG, "width: " + width_tmp + "  height: " + height_tmp + "  scale: " + scale);


        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        bmp = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {

    } catch(OutOfMemoryError e) {
        Debug.i(TAG, "we retry it cause of an OutOfMemoryException");
        return decodeFile(f, size, scale+1);
    } catch(Exception e){
        Debug.w(TAG, e);
    }
    return bmp;
}

当然现在有可能,你会在不同的时间看到同一张图片的不同分辨率——但至少你的画廊不会再崩溃了,你总是展示你能做到的最高分辨率。

于 2011-05-30T11:16:36.993 回答