为什么要使用Fragment?

68
使用Fragment而不是在不同的布局中重用自定义View,有什么优势?
原始博客文章介绍片段中,Dianne Hackborn说:
“[片段]使开发人员更容易编写可以跨越各种屏幕大小扩展的应用程序,超出平台已经提供的功能。”
她接着解释了如何在制作一个平板电脑版应用程序时,在上下文中使用片段来结合同一应用程序的手机版本的两个活动的UI。
但似乎使用自定义视图也可以实现相同的重用。 片段和视图之间的主要区别似乎是它们具有不同的生命周期... Fragment的生命周期是:

onAttach()onCreate()onCreateView()onActivityCreated()onStart()onResume()onPause()onStop()onDestroyView()onDestroy()onDetatch()

View 生命周期包括:

ctoronFinishInflate()onAttachedToWindow()onMeasure()onLayout()onDetatchedFromWindow()

我想听听有经验的开发人员关于使用Fragments与自定义Views将UI分解为可重用部分时所看到的任何好处(如果有)。

6个回答

61

主要原因在于片段比自定义视图更具可重用性。

有时,你无法仅依靠视图创建一个完全封装的UI组件。这是因为有些东西你想放在你的视图中,但却不能,因为只有 Activity 才能处理它们,从而强制将 Activity 和 View 紧密耦合。

以下是一个这样的例子。假设您想创建一个可重用的UI组件,其中包括捕获照片并对其进行某些操作。传统上,您会触发一个意图,启动相机并返回拍摄的图像。

请注意,您的自定义UI组件无法完全封装此功能,因为它将不得不依赖于托管Activity的 startActivityForResult,因为视图不接受活动结果(它们可以通过上下文间接地触发意图)。

现在,如果您想在不同的Activity中重用自定义UI组件,您将需要重复编写 Activity.startActivityForResult 的代码。

而片段则干净地解决了这个问题。

同样,您的片段可以向选项菜单添加项目,这通常只有 Activity 才能够实现。如果自定义视图的状态决定了菜单中的内容,则这可能非常重要。


非常好的答案,我在找到你的之前不得不浏览3-4个类似的SO帖子。 - Martin Konecny
同样地,您的片段可以向选项菜单中添加项目。@numan如果您能稍微解释一下,那将是非常有帮助的。 - Shubham AgaRwal

32

一个Fragment不仅仅是一个视图,事实上它甚至可以完全没有视图。它可以包含各种东西,包括异步任务、各种监听器、文件和数据库访问等等。

想象一下它就像一个小型的Activity,但你可以在屏幕上有多个Fragment,并与它们所有进行交互,同时它们也可互相通信。

例如,您可以在一个Fragment中显示购物车列表,而在另一个Fragment中详细显示当前选定的购物车。然后您可以更改详细视图中某个项目的数量,列表视图会收到通知并更新列表视图中的总价格。 您可以完全协调这样的交互,而在较小的屏幕设备上仍然只有其中之一可见。

我已经将一个大型商业应用程序(> 15个活动)从Activities重构为Fragments以获得良好的平板电脑支持,且如果开始开发新应用程序,我永远不会不使用Fragments。

更新于 2016 年 2 月:虽然上述内容仍然正确,但是Fragment存在复杂性,导致许多人完全避免使用它们。使用较新的模式,如MVC方法和更强大的视图提供了替代方案。正如他们所说..YMMV。


13
但我也可以使用自定义视图来实现你刚才提到的例子。您可以使视图彼此通信并检查操作系统是否正在使用平板电脑或手机布局。这与人们使用片段做的事情是相同的。使用片段而不是视图的好处是什么? - VIBrunazo
1
或许片段提供更多关于过渡、动画的支持,并且对于一些容器例如ViewPager、ActionBar和Tabs是必须的。 - Snicolas
1
@VIBrunazo 请看下面的答案。https://dev59.com/Rmkw5IYBdhLWcg3wirCs#14912608 - numan salati

9

一些描述:

把Activity想象成一个托盘,它可以放置一个大蛋糕。 Fragment则是将同一个蛋糕切成小块的容器。 每一小块都包含自己的逻辑(监听器等)。 总体上,它们与一个大蛋糕几乎没有什么不同。

好处:

  1. 当你的托盘无法装下一个大蛋糕时(屏幕太小),你可以轻松地使用几个托盘(Activity)来承载它们,而无需将你的逻辑移动到新的Activity中。

  2. 更好的可重用性。我有一些实例,可以完全在另一个应用程序中重用一个Fragment。你可能会说一个自定义视图也可以做到这一点。但是请参考第1点,我只需要进行少量的布局更改就可以重用它,但对于自定义视图来说,它必须找到一种方法将其插入到布局和代码中。

  3. 从某种意义上说,它是在Android编程中组织UI逻辑的一种更面向对象的方式。当你有一个功能(例如屏幕上的一个新分区),你创建一个新的Fragment类,并对现有的Activity类进行小的修改。然而,如果你只使用Activity进行编程,你将需要添加逻辑并对已测试的类进行大量修改。

以上仅供参考。 :)


2
但是你可以很容易地将逻辑封装在每个蛋糕片中,而无需使用片段。我可以创建一个漂亮干净的活动,只需要一两行代码来创建和实例化一个蛋糕片。所有的逻辑都将在蛋糕类中。 - Jono
总的来说,片段只是一种将XML与相关Java源代码耦合并将其放在一个独立于外部世界的包中的方法,对吧?我的意思是,除此之外,如果我们可以想象出图形也是从源代码创建的情况,我们可以使用常规类。另外,顺便提一下,在Scala中有一种非常好的方法可以通过使用特质来解决这些问题! - George Pligoropoulos
1
问题实际上是关于碎片与视图的区别,而不是碎片与活动的区别。 - user3614314
确实,这才是真正的问题.. 片段 vs 视图。而且在2016年之后,我很少会认为片段是不可替代的。 - user3833732

4
生命周期方法可能是你最大的提示。如果你想一想,它们与活动生命周期密切相关(在一些钩子进入活动和视图中)。事实上,在你提供链接的文章中,Hackborn说: > 在某些方面,你可以将Fragment看作是一个迷你Activity。
像软件设计/开发中的许多事情一样,有许多方法可以做到这一点。你可以把很多东西放在一个视图里,但是在不同的类中保持不同关注点分离是一个好习惯。这种情况下的经典模式是MVC,它适用于此场景。你不希望在视图中加入太多控制器逻辑。最好将其保留在类似于活动和现在的片段这样的控制器类中。这就是为什么片段的生命周期更像活动而不是视图--它被设计用来促进这种组织方式的原因。

2
似乎Fragments和activities在MVC中都扮演控制器的角色。为什么不使用POJO类来分割控制器,而不是Fragments呢? - avh4

2
我曾经接触过Fragments,但发现它们并不是很有用(请参考这篇文章)。根据我所了解的,Fragment实际上是一个带有访问Activity上下文的对象的高级词汇。在我的工作中,我喜欢忽略Fragments,而是自己创建这些对象来构建非常大、非常复杂的应用程序,将Activity传递给构造函数,而不是Context。然而,使用Fragments的一个主要好处是,它们被View布局系统支持 - 因此,如果你在布局中使用Android xml,你可以很容易地将它们添加到其中。

0

使用自定义视图比在活动中使用片段要麻烦得多。如果您决定同时使用活动和自定义视图,则必须创建自定义视图,然后在活动中实现相同的活动生命周期方法(片段使用非常类似的生命周期)。

使用片段还允许将组件分离到它们自己的类(片段)中,而不是在单个活动中具有太多逻辑。我们用一个例子来解释:

假设你正在实现一款杂志阅读应用程序。使用片段,您可以创建一个名为ArticleList的片段,用于显示文章列表,以及另一个名为ArticleDisplay的片段,用于处理显示内容的逻辑。然后,您可以使用片段工具指定这些片段应如何交互,以便在手机上,您可以使用全屏幕空间来显示ArticleDisplay,而在平板电脑上,则可以并排显示片段。

如果您尝试在活动/自定义视图中执行此操作,则必须在笨重的活动中拥有两个片段的逻辑,必须编写自定义视图,并且必须调试此笨重的怪物。

片段通常是编写应用程序的一种更复杂和强大的方式。它们可以做任何活动可以做的事情,甚至更多。如果您不需要额外的功能,则默认设置可能会让您到达所需的位置,并减少工作量。


2
就单块活动而言,为什么要使用Fragments而不是将控制器代码拆分到其他POJO控制器类中? - avh4
这对Fragment中包含的逻辑是起作用的(你可能应该在Activity和Fragment中都这样做),但你仍然会遇到一个问题,即你有一个单一方法调用作为至少两个控制流程的入口点,最好将它们分开。你只能在一个地方重写Android用来与你的类通信的方法调用,因此如果你有一个庞大的Activity,你需要为两组对话框中的每一个“屏幕”调用一个OnCreateDialog()。 - Chris Bye
我还应该补充一点,使用Fragment而不是Activity几乎没有任何缺点。很可能您不需要编写任何额外的代码即可获得与Fragment相同的行为,如果需要,您还可以做更多的事情。 - Chris Bye
@avh 请看下面我的回答:https://dev59.com/Rmkw5IYBdhLWcg3wirCs#14912608 - numan salati

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