是否存在具有 View 回收的 Gallery 小部件的替代品?

Posted

技术标签:

【中文标题】是否存在具有 View 回收的 Gallery 小部件的替代品?【英文标题】:Does a replacement for Gallery widget with View recycling exist? 【发布时间】:2011-08-12 23:46:31 【问题描述】:

android 上的默认图库小部件不会回收视图 - 每次调用新位置的视图时,小部件总是调用适配器的 getView 方法,并将 convertView 设置为 null。

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

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

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

【问题讨论】:

如果没有人有其他解决方案,您可以将源代码获取到Gallery,修改它以适应它,然后使用它。我在代码中看到了您所指的内容。我不知道为什么makeAndAddView() 是这样写的。 @CommonsWare 干杯 - 我已经开始这样做了,但它很丑陋并且意味着复制和编辑相当多的类,因为它使用了一些受保护的成员和方法,所以我只是想知道是否有人有更好的想法。 【参考方案1】:

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

AdapterView AbsSpinner

但这些都很简单。

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

RecycleBin (AbsSpinner)

将对象一个接一个地放入回收站,而不是按照 定位 从回收器底部检索对象,无论 职位要求 现有实现假定适配器中的每个不同位置 产生了独特的观点。只有当您的图库包含 只有一种类型的项目,如果不是,您需要根据项目类型添加某种键 以及所需类型的数量

Gallery

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

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

Here is the code(在http://en.wikipedia.org/wiki/WTFPL下的公共领域2013/08/07)

【讨论】:

您介意发布指向 Gallery 和您提到的其他两个源文件的链接吗? 太长了,无法在一个答案中发布所有课程,所以我把它们放在这里:pastebin.com/FWyYTt4D 非常感谢您发布此信息!与内置的 Gallery Widget 相比,它的效果很好。很遗憾,我们不得不采取如此极端的措施来获得一个正常运转的回收站。如果我意识到这将是多么痛苦,我会选择一个不同的小部件而不是画廊。您修改后的画廊工作并解决了我的问题,但再次感谢! 我不能使用你提供的代码...有多个类转换异常,而且 onItemclickListener 也产生了问题... 03-05 12:16:00.545: E/AndroidRuntime(30246) : java.lang.ClassCastException: android.widget.Gallery$LayoutParams 03-05 12:16:00.545: E/AndroidRuntime(30246): at .EcoGallery.setUpChild(EcoGallery.java:773) 03-05 12:16:00.545 : E/AndroidRuntime(30246): at com.exiticlabs.arsenallwp.EcoGallery.makeAndAddView(EcoGallery.java:752) 03-05 12:16:00.545: E/AndroidRuntime(30246): at .EcoGallery.layout(EcoGallery.java :646) .EcoGallery.onLayout(EcoGallery.java:362) @Akos 既然你问了,我将代码放在en.wikipedia.org/wiki/WTFPL下的公共域中【参考方案2】:

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

https://github.com/falnatsheh/EcoGallery

【讨论】:

EcoGallery 基于(已弃用)Android Gallery,它基本上是带有一些更改的 Android Gallery 源代码(但它表现出与旧 Gallery 相同的错误),尽管它具有更好的 recicles 视图。但是,如果您在后台下载图像,“跳跃”画廊问题仍然存在。 不确定什么是“跳跃”错误,但无论如何我都不喜欢画廊视图,你说得对,这个项目是基于画廊的原始代码,但他们声称尝试修复此视图。我认为至少视图回收机制是固定的。 是的,这个版本的画廊在回收视图方面做得更好(尽管不如列表视图好)。最大的问题(原始图库已经过并且即使在被弃用后也从未修复过)是当您在后台加载图像时(就像您应该这样做的那样),它们会触发布局,这会导致图库捕捉“最接近的图像”到中心。当您滚动并加载项目(并放入画廊)时,这会导致一直跳跃。有一些解决方法可以解决这个问题,但它很hacky。一旦加载了imgs,画廊就可以正常工作:) 我仍然不确定你在说什么。如果正在加载图像并且视图的大小是基于它们的,当然它们会导致奇怪的事情发生,因为视图的大小会发生变化。 listViews 也会发生这种情况。您应该在项目上使用固定尺寸以避免这种情况。无论如何,关于图书馆,我已经在大约一个月前尝试过它并放弃了它,因为我发现了一个崩溃(只有在大量快速滚动之后才会发生)。这就是为什么我认为如果您必须使用画廊,请改用这个。 "listViews 也会发生这种情况。您应该在项目上使用固定大小以避免这种情况。"嗯,不,这不会发生在列表视图上。你从哪里得到这个想法的?发生这种情况是因为原始画廊将项目捕捉到 onLayout 的中心(当 ImageView 执行 setXXXXBackground() 时会调用它。它永远是画廊中的一个错误,并且可能是谷歌决定放弃画廊的众多原因之一并从头开始(可以,但不是 100% 相似的替代方案)ViewPager 和 Horizo​​ntalScrollableView)。【参考方案3】:

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

【讨论】:

【参考方案4】:

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

我称它为TimelineGallery,它与与图库 相同,但它可以平滑滚动,并且在异步加载图像时不会做奇怪的事情。

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

原始代码、版权等属于 Google,因此请怪他们制作了如此糟糕的小部件。

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

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

如果您仍想继续使用此“图库”,请放心,它可以工作,但它可能会导致您的应用崩溃并让您感到沮丧。

【讨论】:

【参考方案5】:

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;

当然,现在您可以在不同时间看到同一张图片的不同分辨率 - 但至少您的图库不会再崩溃,并且您始终会显示尽可能高的分辨率。

【讨论】:

以上是关于是否存在具有 View 回收的 Gallery 小部件的替代品?的主要内容,如果未能解决你的问题,请参考以下文章

王立平--Gallery:实现图片的左右滑动

Android 使用Gallery组件实现图片播放预览

小米手机米柚有个文件叫Gallery,但是显示是空文件,但却内存很大,为啥?

NexGen Gallery-删除“放映幻灯片”链接

Blogger的Gallery小部件显示当前帖子的所有图像

具有两个联接的聚合 (MySQL)