Fragment,简称碎片,是android3.0的产物,在android3.0之前解决手机、平板电脑的适配问题是很头疼的,对ActivityGroup有印象的朋友,应该能深深的体会到ActivityGroup包裹的多个Activity之间切换等一系列的性能问题。由此Fragment诞生了。为了兼容低版本,support-v4库中也开发了一套Fragment API,最低兼容Android 1.6。
过去support-v4库是一个jar包,24.2.0版本开始,将support-v4库模块化为多个jar包,包含:support-fragment, support-ui, support-media-compat等,这么做是为了减少APK包大小,你需要用哪个模块就引入哪个模块。
如果想引入整个support-v4库,则compile ‘com.android.support:support-v4:x.x.x’,如果只想引入support-fragment库,则com.android.support:support-fragment:x.x.x。
因为support库是不断更新的,因此建议使用support库中的android.support.v4.app.Fragment,而不要用系统自带的android.app.Fragment。而如果要使用support库的Fragment,Activity必须要继承FragmentActivity(AppCompatActivity是FragmentActivity的子类)。
Fragment官方定义:
Android 3.0系统只针对平板电脑,且闭源,那时候针对手机和针对平板是两套源代码,后来Android 4.0时整合了手机和平板的源码,因此市面上很难看到Android 3.0系统。
Fragment的优势:
- 模块化(Modularity):我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中。
- 可重用(Reusability):多个Activity可以重用一个Fragment。
- 可适配(Adaptability):根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,这样用户体验更好。
Nested Fragment(Fragment内部嵌套Fragment的能力)是Android 4.2提出的,support-fragment库可以兼容到1.6。通过getChildFragmentManager()能够获得子Fragment的FragmentManager,在子Fragment中可以通过getParentFragment()获得父Fragment。
详见:《Fragment生命周期详解》
1. onCreateView
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View mainView = inflater.inflate(R.layout.fragment_main, container, false);
return mainView;
}
Fragment有很多可以复写的方法,其中最常用的就是onCreateView(LayoutInflater, ViewGroup, Bundle),该方法返回Fragment的UI布局。需要注意的是inflate()的第三个参数是false,因为在Fragment内部实现中,会把该布局添加到container中,如果设为true,那么就会重复做两次添加,则会抛如下异常:
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.
当然,我们也可以把第二次参数改为null。
2. onAttach
@Override
public void onAttach(Context context) {
super.onAttach(context);
activity = (MainActivity) context;
}
如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据。
我们可以在Fragment的onAttach()中通过getArguments()获得传进来的参数,并在之后使用这些参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象。
3. 把Fragment添加到Activity中
创建完Fragment后,接下来就是把Fragment添加到Activity中。在Activity中添加Fragment的方式有两种:
虽然Fragment能在XML中添加,但是这只是一个语法糖而已,Fragment并不是一个View,而是和Activity同一层次的。
这里只给出动态添加的方式。首先Activity需要有一个容器存放Fragment。
BaseFragment fragment = new MainFragment();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.main_activity_layout, fragment)
.commit();
上面例子的容器是 R.id.main_activity_layout 。
4. 注意事项
5. 常见异常
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
该异常出现的原因是:commit()在onSaveInstanceState()后调用。首先,onSaveInstanceState()在onPause()之后,onStop()之前调用;onRestoreInstanceState()在onStart()之后,onResume()之前(请参阅《Fragment生命周期详解》)。
因此避免出现该异常的方案有:
详情查阅《Fragment通信详解》
修订:2018/05/16