是否存在具有 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
)使用反射 (ugh) 修改
Gallery
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 和 HorizontalScrollableView)。【参考方案3】:我使用了来自http://code.google.com/p/android/issues/detail?id=3376#c19的补丁
【讨论】:
【参考方案4】:派对迟到了,但我已经修改了 EcoGallery 以做更多的事情(并避免一些崩溃)。
我称它为TimelineGallery,它与与图库 相同,但它可以平滑滚动,并且在异步加载图像时不会做奇怪的事情。
为了演示它,示例使用了 Picasso 和 PullToRefresh。
原始代码、版权等属于 Google,因此请怪他们制作了如此糟糕的小部件。
最后说明:我不建议使用图库,它很旧、有问题、很老套,而且可能永远不会被维护。问题不在于修复它的错误,问题在于 Gallery 的整个架构是错误的,因此,如果不引入更多的 hack,就不可能修复它。
Google 意识到了这一点并弃用了它。使用 ViewPager 或 HorizontalScrollList 并处理各自的限制。
如果您仍想继续使用此“图库”,请放心,它可以工作,但它可能会导致您的应用崩溃并让您感到沮丧。
【讨论】:
【参考方案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 小部件的替代品?的主要内容,如果未能解决你的问题,请参考以下文章