安卓 - 使用ViewStub值得吗?

18

我有一个ListView,每行包含大约10个ImageButton。其中大多数按钮的可见性为Gone,只在非常罕见的情况下显示。我想知道是否值得用ViewStubs替换这些ImageButton以避免为ListView的所有行都加载它们(和它们包含的图像)。然而,它们的可见性设置为“Gone”,因此我不确定加载它们会产生什么影响。它们的图像实际上是否被加载了?

请注意,我正在讨论将8个ImageButton替换为8个ViewStubs,而不是1个。

谢谢


2
我不知道答案,但我想知道为什么你要这样做。将一个ViewContainer(例如LinearLayout)包括进去,然后用Adapter添加所需的图像到其中,这不是更直观吗?这样就不会有不必要的视图附加到你的布局上了。 - Endzeit
这个想法只是为了将所有的视图声明保留在xml中,并根据需要在适配器中仅更改元素的可见性。所以您基本上建议在适配器中添加/删除它们,而不是更改它们的可见性?当我们通过convertView在getView()中重用视图时,您会建议仅从该视图中删除所有内容并重新开始,还是检查其中的内容并相应地进行修改? - sakis kaliakoudas
7个回答

12

ViewStub是一种愚蠢且轻量级的视图。它没有尺寸,不会绘制任何内容,也不参与任何布局。这意味着ViewStub非常便宜,可以很容易地在视图层次结构中保留。最好将ViewStub描述为“惰性包含”。只有当您决定这样做时,引用的布局才会被填充和添加到用户界面。

有时您的布局可能需要很少使用的复杂视图。无论是项目详细信息、进度指示器还是撤消消息,都可以通过仅在需要时加载视图来减少内存使用和加速渲染。

简单来说,ViewStub用于提高布局渲染效率。通过使用ViewStub,可以手动创建视图,但不会将其添加到视图层次结构中。在运行时可以轻松填充,当ViewStub被填充时,viewstub的内容将被替换为viewstub中定义的布局。

ViewStub只有在实际使用/需要它时才会加载,即当您将其可见性设置为VISIBLE(实际可见)或INVISIBLE(仍未可见,但其大小不再为0)时。 ViewStub是一种很好的优化,因为您可以在任何地方拥有具有大量小视图或标题的复杂布局,并且仍然使您的活动快速加载。一旦使用其中一个视图,它就会被加载。

您必须首先在布局中添加ViewStub,然后才能将其填充到另一个视图中。

注意:ViewStub的一个缺点是它当前不支持要填充的布局中的<merge/>标记。同时,ViewStub不能多次使用。如果不需要长期引用ViewStub,最好在充气后将其置空,以便GC可以回收它。

假设您的ViewStub ID为view_stub,则需要在activity中执行以下操作:

ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
View inflatedView = viewStub.inflate();
ImageButton button = (ImageButton) inflatedView.findViewById(R.id.button);

现在你可以随心所欲地使用按钮 :) 即inflate方法返回包含来自XML文件的实际元素的存根布局。

当然,你始终可以拥有onClick XML属性或动态调用它。

Is a ViewStub worth it?
->For the scenarios that you are specifying, I think `ViewStub` will be worth-shot.  
请见下面关于 ViewStub 的链接:

http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html

http://developer.android.com/reference/android/view/ViewStub.html

http://developer.android.com/training/improving-layouts/loading-ondemand.html

除了使用 ViewStub,您还可以尝试使用 <include> 标签。 <include/> 将会像整个文件只是一个单独的大文件一样,将 xml 内容包含在您的基本 xml 文件中。这是在不同布局之间共享布局部件的好方法。

Android 中 <include> 和 <ViewStub> 的区别


7

编辑:我刚注意到Endzeit在我之前提到了类似的方向。

首先,我会对带有视图和没有视图的膨胀代码进行一些基准测试——只需注释掉适配器代码,这样它就不会尝试访问不存在的视图。

如果从布局中删除视图确实给您带来了必要的改进,而且您说这些视图只在您正在检查的罕见情况下存在,则可以创建它们并根据需要添加/删除它们(使用viewholder引用它们),而不是膨胀那些视图甚至使用视图存根。

您甚至可以进一步做一些懒惰的创建视图,类似于懒加载图像,但我建议您再次运行一些基准测试。

我会使用ViewStubs来加载复杂的布局而不是简单的ImageButton。

编辑2:

查看ViewStub inflate命令,当它需要可见时,它会将给定的布局膨胀然后将其添加到父布局中——由于您要添加一个简单的ImageButton,因此您可以通过在代码中添加ImageButton而不是ViewStub来提高性能。 http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/view/ViewStub.java#ViewStub.inflate%28%29


你最后一句话是我提出这个问题的主要关注点!我将使用TraceView运行几个比较,看看会有什么结果。干杯 - sakis kaliakoudas
1
增加了一些解释,说明为什么在运行时添加ImageButton比使用ViewStub更好。 - Raanan
你能否看一下我在评论区顶部的问题? - sakis kaliakoudas
1
是的,我的想法是在适配器中添加/删除视图-在我看来,这是一种有效的方法,特别是因为你说大多数时候视图都不存在。 - Raanan
1
您可以轻松检查持有者是否引用了现有的ImageButton,然后根据需要删除或添加它并更新其内容。 - Raanan

4
根据Google的官方文档这里
ViewStub是一个轻量级视图,没有任何维度,不会绘制任何东西或参与布局。因此,它很便宜可以填充和保留在视图层次结构中。每个ViewStub只需要包含android:layout属性以指定要填充的布局。
为了实验这个,我创建了一个示例项目并将ViewStub添加到布局层次结构中。在运行布局检查器时,我可以看到ViewStub的所有布局属性都为零。

enter image description here

让我们将其与具有10个隐藏按钮的布局进行比较。实际上,这意味着布局层次结构中有10个隐藏按钮,它们位于布局层次结构中并占用一定的内存。在层次结构中保留ViewStub是很便宜的,因为它不占用太多内存,同时膨胀也很便宜。
我的最终结论是,在拥有复杂视图且很少膨胀时广泛使用ViewStub,因为它确实有助于节省内存并改善视图膨胀时间。

2
使用Android Studio中的Android监视器的内存选项卡(Android监视器按钮应位于底部栏),您可以自行检查:
  • 在使用不可见按钮运行应用程序时查看内存使用情况
  • 在使用可见按钮运行应用程序时查看内存使用情况
如果有任何差异,则可以得出结论:当视图消失时,并非所有内容都是预加载的。当然,您还可以将其与ViewStub实现进行比较,以检查是否可以帮助减少内存使用量。

2
简而言之,使用自定义视图而不是viewstub。
我们现在遇到了类似的情况,之前也尝试过使用viewstub,但listview速度稍微快一些。但是当涉及到8个viewstub时,我认为使用viewstub来避免充气太多小部件并不是一个好主意。
由于您(和我们)有一个逻辑来控制是否显示10个按钮,为什么不只定义一个自定义视图,并根据不同状态机绘制不同的按钮呢?这样更快,完全不需要充气,逻辑也更易控制。我们现在正在使用这种方法加速listview,效果不错。

你是在说ViewGroup吗? - sakis kaliakoudas
@sakis 在我们的一个案例中,我们需要展示6个按钮,每个按钮对应一个状态。我们定义了一个继承自View类的MyView,在它的onDraw方法中绘制按钮(它只是一个带有圆角的矩形)和其他控件,如TextView。将MyView设置为可点击并响应触摸事件(获取点击坐标并判断是否在矩形内)。如果需要任何点击效果,只需在更改矩形属性时添加动画即可。 - oscarthecat
是的,一开始我也是这么想的,然后我开始考虑点击监听器和处理所有这些:P 但如果你有性能问题,这样做确实是最高效的。感谢澄清! - sakis kaliakoudas
我很高兴它有帮助 ^_^ - oscarthecat

1
当你将一个视图的可见性设置为gone时,这意味着该视图是不可见的,并且在布局中不占用任何空间,但其数据已加载到其中。
现在,为了提高性能,ListView会删除看不见的或超出屏幕范围的视图。
ViewStub是一个不可见的、大小为零的视图,可以用于在运行时懒惰地填充布局资源。
所以,我认为如果你想听我的意见,我更喜欢使用GONE可见性的视图,而不是使用ViewStub和创建和填充等很多逻辑。
但另一方面,当你填充视图时,渲染性能就会变得很重要。
我猜测,相对于从XML或通过更改可见性来填充视图,使用ViewStub来填充视图更加便宜。ViewStub特别用于需要添加/删除(无限)视图(例如,向给定联系人添加电话号码)。希望这就是你要找的东西。
参考:ViewStub vs. View.GONE

这里有一个关于DDMS的简短介绍: http://magicmicky.github.io/android_development/benchmark-using-traceview/


-1
使用ViewStub代替ImageButton。
这是因为:
1. ViewStub默认是零大小视图,而ImageButton不是。
2. ViewStub自然是一个不可见的视图。它的性能比ImageButton更好,因为它只在状态变为可见时才加载运行时。

您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - sakis kaliakoudas
这个问题与此案例完全不同。如果您设置了视图的可见性visibility=gone,那么将其恢复为visibility=visible是您的责任,无论是视图存根还是图像按钮都是一样的。 - Usman Anwer

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