这里主要就是讨论一个应用程序的UI组件,是全用Activity还是全用Fragment,或者是二者皆有,以及使用Activity和Fragment的一些注意事项。
从API 11,Honeycomb开始,Google在Android之中加入了Fragment,一个轻量级的,可复用的,模块化的UI组件,并且官方也极力的推荐要尽可能的使用Fragment实现UI,尽量避免使用Activity,但是官方并没有详尽的解析为什么要这样做,因此,引发了很多人的困惑和网络上的争论
StackOverflow上面有很多类似的讨论,比如这个和这个,以及这篇博客和这篇。
总体的来讲,也都是推荐使用Fragment,并尽可能的使用Fragment,除非你必须使用Activity,否则就使用Fragment。
要想真正的理解什么时候该用Activity什么时候该用Fragment,就要从根本上理解它们之间到底有什么区别,以及各自所能做的事情:
Activity是Android系统的四大核心组件之一,是唯一用户可见的组件,是可视化应用程序的基础,同时也是应用程序的主要入口。 它的优点是:
Activity的缺点:
所以,因为它有缺点,所以它适合做为第一级组件,而不适合到处都用。也因此有了Fragment的诞生。
Fragment号称是轻量级的Activity,它有着Activity的部分功能,比如有生命周期,可以显示UI元素,可以有控制逻辑,它有很多优点:
可以看出Fragment是实现可复用的,模块化UI的良好组件,熟悉iOS开发的人应该知道,这个Fragment跟iOS中的UIViewController是很相似的,都是MVC模式中的负责协调和控制的Controller。
那么,到此,我想我们应该能够总结出,到底什么时候该用Activity,什么时候该用Fragment了:
除此外,就使用Fragment吧。因此,对于一个应用程序的UI架构就是一个Host的Activity+Fragment的方式来做。
Activity是学习Android开发的第一堂课,我相信有过Android开发经验的人对Activity必定非常的熟悉了,对于使用Activity要注意:
Fragment是由FragmentManager来管理的,每一个Activity有一个FragmentManager,管理着一个Fragment的栈,所以,Activity是系统级别的,由系统来管理ActivityManager,栈也是系统范围的。而Fragment则是每个Activity范围内的。
同一个Activity中,只能有一个ID或TAG标识的Fragment实例。
这很容易理解,同一个范围内,有标识的实例肯定是要唯一才行(否则还要标识干嘛)这个在布局中经常犯错,在布局中写Fragment最好不要加ID或者TAG,否则很容易出现不允许创建的错误。我的原则是如果放在布局中,就不要加ID和TAG;如果需要ID和TAG就全用代码控制。创建新实例前先到FragmentManager中查找一番,这也正是有标识的意义所在。
一个Activity中有一个Fragment池,实例不一定会被销毁,可能会保存在池中。这个跟第一点差不多。就好比系统会缓存Activity的实例一样,FragmentManager也会缓存Fragment实例,以方便和加速再次显示。
如前所述,FragmentManager的作用范围是整个Activity,所以,某一个布局ID,不能重复被Fragment替换。
通常显示Fragment有二种方式,一种是层叠到某个布局上,或者把某个布局上面的Fragment替换掉,但是这个布局不能出现二次,比如布局A中有ID为id的区域,要显示为Fragment,此布局A,只能在一个Activity中显示一个,否则第二个id区域不能被Fragment成功替换。因为虽有二个ID布局的实例,但ID是相同的,对FragmentManager来说是一样的,它会认为只有一个,因为它看的是布局的ID,而不是布局的实例。
Fragment的生命周期反应Activity的生命周期。
Fragment在显示和退出时会走一遍完整的生命周期。此外,正在显示时,就跟Activity的一样,Activity被onPause,里面的Fragment就onPause,以此类推,由此带来的问题就是,比如你在onStart()里面做了一些事情,那么,当宿主Activity被挡住,又出现时(比如接了个电话),Fragment的onStart也会被高到,所以你要想到,这些生命周期不单单在显示和退出时会走到。
Fragment的对用户可见性。
这个问题出现在有Fragment栈的时候,也就是说每个Fragment不知道自己是否真的对用户可见。比如现在是Fragment A,又在其上面显示了Fragment B,当B显示后,A并不知道自己上面还有一个,也不知道自己对用户不可见了,同样再有一个C,B也不知。C退出后,B依然不知自己已在栈顶,对用户可见,B退后,A也不知。也就是说Fragment显示或者退出,栈里的其他Fragment无法感知。这点就不如Activity,a被b盖住后,a会走到onStop(),同样c显示后,b也能通过onStop()感知。Fragment可以从FragmentManager监听BackStackState的变化,但它只告诉你Stack变了,不告诉你是多了,还是少,还有你处的位置。有一个解决方案就是,记录页面的Path深度,再跟Fragment所在的Stack深度来比较,如果一致,那么这个Fragment就在栈顶。因为每个页面的Path深度是固定的,而Stack深度是不变化的,所以这个能准确的判断Fragment是否对用户可见,当然,这个仅针对整个页面有效,对于布局中的一个区域是无效的。
Fragment的事件传递。
对于层叠的Fragment,其实就相当于在一个FrameLayout里面加上一堆的View,所以,如果处于顶层的Fragment没处理点击事件,那么事件就会向下层传递,直到事件被处理。比如有二个Fragment A和B,B在A上面,B只有一个简单的TextView且没处理事件,那么点击B时,会发现A里的View处理了事件。这个对于Activity也不会发生,因为事件不能跨窗体传播,上面的Activity没处理事件,也不会传给下面的Activity,即使它可见。解决之法,就是让上面的Fragment的根布局吃掉事件,为每个根ViewGroup添加onClick=“true”。
与第三方Activity交互。与第三方交互,仍要采用Android的标准startActivityForResult()和onActivityResult()这二个方法来进行。但对于Fragment有些事情需要注意,Fragment也有这二个方法,但是为了能正确的让Fragment收到onActivityResult(),需要: