生命周期
Activity和Fragment比较
其实, 上图所体现的Fragment的生命周期只是一部分, 真正的Fragment生命周期更加复杂, 如下图:
生命周期主要的方法回调时机
- onAttach()
关联Activity时调用 - onCreate()
创建Fragment时调用,在这里必须初始化Fragment的基础组件 - onCreateView()
Fragment要绘制自己的界面时调用,这个方法必须返回Fragment的layout,也可以返回null(表示没有界面) - onActivityCreated()
当Activity对象完成自己的onCreate方法时调用 - onStart()
Fragment的UI可见时调用 - onResume()
Fragment的UI可交互时调用 - onPause()
Fragment 可见但不可交互时调用 - onStop()
Fragment 完全不可见时调用 - onDestroyView()
Fragment 移除视图时调用 - onDestroy()
清理View资源时调用 - onDetach()
失去Activity关联时调用
Fragment之所以会出现一系列乱七八槽的问题,终究是因为没有弄清楚其生命周期.
Fragment的使用
- Fragment的使用有两种, 一种是静态使用, 另外一种则是动态使用.
鸿洋:Android Fragment 真正的完全解析
鸿洋大神已经很详细的说明了Fragment的使用,还包括了Fragment和Activity之间的通信,Fragment的回退栈等, 所以以下内容仅为本人的一些总结.
静态使用
将Fragment当作普通的view一样使用, 只需要在Activity布局声明Fragment的节点
<fragment android:id="@+id/id_fragment_title" android:name="com.zhy.zhy_fragments.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp" />
由代码可以看出, 我们需要自定义Fragment的类, 所以我们需要创建一个新的类, 继承Fragment, 并且还要重写onCreateView决定Fragment的布局.
View view = inflater.inflate(R.layout.fragment_title, container, false);//false表示不替换根布局
这样我们在Activity里就只需要加载它的布局, 不需要对布局进行任何的管理, 因为Fragment都有它自己的生命周期和事件的处理, 这样Activity就变得很干净了呢!
动态使用
因为静态使用局限性太多, 动态的使用Fragment才是我们实际需要的, 例如添加、更新、以及删除和替换 Fragment.
1. 在我们想要使用Fragment的地方声明一个FrameLayout用于放置Fragment
<FrameLayout
android:id="@+id/id_content"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
2. 依然需要创建新的类继承Fragment并且加载它的布局.
3. Activity需要做的事情就相比静态使用要多一些了,总共需要5步,必不可少.
- 拿到FragmentManager
getFragmentManager()
(兼容性写法getSupportFragmentManager()
). - 用FragmentManager开启事务
beginTransaction()
,返回FragmentTransaction对象 (事务是用于解决数据库并发的问题,这里先不关心). - 拥有Fragment对象
new Fragment()
. - 事务替换
ft.replace(R.id.container, fragment)
.即替换容器中的内容. - 事务提交
ft.commit();
.
链式编程可以简单写成getFragmentManager().beginTransaction().replace(容器id,希望显示的fragment).commit();
FragmentTransaction常用API
Fragment的操作主要都是依靠FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();
开启一个事务
add()
往Activity布局的容器中添加一个Fragment
remove()
从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
replace()
使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
hide()
隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
show()
显示之前隐藏的Fragment
detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护.
remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁, 如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach, 因为不需要重新创建实例.
attach()
重建view视图,附加到UI上并显示.
commit()
提交一个事务,特别需要注意的是,如果遇到Activity状态不一致:State loss这样的错误. 主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用.
Fragment回退栈
在提交事务之后, 即当前Fragment已经不能看见, 如果你想通过返回键再回退到当前Fragment, 就需要在提交事务之前和replace之后, 将当前Fragment通过事务调用addToBackStack(String)添加到了回退栈. 但是replace之后, 当前Fragment的视图层次依然会被销毁,即会调用onDestoryView和onCreateView, View视图所保存的内容将会丢失, 所以如果不希望视图层被销毁, 就不能调用replace方法, 而是调用 hide 和 add方法.
Fragment的回退栈是由FragmentManager来管理的.
管理Fragment回退栈
- getSupportFragmentManager().getBackStackEntryCount() -获取回退栈中实体数量
- getSupportFragmentManager().popBackStack(String name, int flags) -根据name立刻显示对应的Fragment, 在其栈之上的fragment都会被销毁.
- getSupportFragmentManager().popBackStack(int id, int flags) -同理
Fragment的通信
从鸿洋: Android Fragment 真正的完全解析(下)中可以看出, 利用Fragment来开启另外一个Fragment的方式是不可取的. 当需要从一个Fragement里面的内容中跳转到另外一个Fragment, 应当由Fragment的管理者Activity来操作.
总的来说, 为了降低Fragment与Activity的耦合, Fragment和Activity之间的通信基本上是基于接口的回调, 这是我们在使用时所推荐使用的.
使用Fragment常见问题
- 点击返回键出现白板
不需要将所有的Fragment都添加到了回退栈. - 两个Fragment重叠在一起
- 当Fragment长时间置于后台, 系统有可能会回收掉, 当你重新打开, 即Activity重新启动时, 又重新绘制了一次Fragment, 所以就会出现重叠.
- 当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建, 这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment
- 解决: 检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建. 和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以.