1. 什么是Fragment?
可以简单的理解为,Fragment是显示在Activit中的Activity。它可以显示在Activity中,然后它也可以显示出一些内容。因为它拥有自己的生命周期,可以接受处理用户事件,并且你可以在一个Activity中动态的添加、替换、移除不同的Fragment,因此对于信息的展示具有很大的便利性。
2. Fragment生命周期
因为Fragment是依附于Activity存在的,因此它的生命周期收到Activity的生命周期影响。
Fragment比Activity多了几个生命周期的回调方法
- onAttact(Activity) 当Fragment与Activity发生关联的时候调用。
- onCreateView(LayoutInflater,ViewGroup,Bundle)创建该Fragmant的视图。
- onActivityCreateed(Bundle) 对Activity的onCreate方法返回时调用。
- onDestroyView()与onCreateView()方法相对应,当Fragment的视图被移除时调用。
- onDetach()与onAttach()方法相对应,当Fragment与Activity取消关联时调用。
注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对应方法的实现。
3. Fragment的使用方式
3.1. 静态使用
① 创建一个类继承Fragment,重写OonCreateView方法,并确定Fragment显示的布局。
② 在Activity中申明该类,与普通的View对象一样。
public class MyFragment extends Fragemnt{
public View onCreateView(LayoutInflater inflater,ViewGroup
container,Bundle savedInstanceState){
/**
* 参数1:布局文件的id
* 参数2:容器
* 参数3:是否将这个生成的View添加到这个容器中去
* 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
**/
View v = inflater.inflate(R.layout.item_fragment, contai ner, false);
return v;
}
}
③ Activity对应的布局文件中使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/ android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.usher.fragment.MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="Good Boy" />
<fragment
android:id="@+id/myfragment"
android:name="com.usher.fragment.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
3.2 动态使用
核心代码
// 初始化FragmentManager对象
FragmentManager fragmentManager = getSupportFragmentManager();
// 使用FragmentManager对象用来开启一个Fragment事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 显示FirstFragment
transaction.add(R.id.fl_content, new FirstFragment()).commit();
// 切换显示
transaction.replace(R.id.myframelayout, fragment 1).commit();
- transaction.add() 向Activity中添加一个Fragment
- transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁
- transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove() 然后add()的合体
- transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
- transaction.show() 显示之前隐藏的Fragment
- detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由 FragmentManager维护
- attach() 重建view视图,附加到UI上并显示
- ransatcion.commit() 提交事务
在add/replace/hide/show以后都要commit其效果才会在屏幕上显示出来
4. 什么是Fragment的回退栈
Fragment的回退栈是用来保存每一次Fragment事务发生的变化,如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则会退出当前Activity。
5. Fragment与Activity之间的通信
Fragment依附于Activity存在,因此与Activity之间的通信可以归纳为以下几点:
- 如果Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment
的public方法。
- 如果Activity中未保存任何Fragment的引用,那么每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者getFragmentManager.findFragementById()获得任何Fragment示例,然后进行操作。
- Fragment中可以通过getActicity()得到当前绑定的Activity的实例,然后进行操作。
6. Fragment与Activity通信的优化
因为要考虑Fragment的复用,所以必须降低Fragment与Activity之间的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
虽然Fragment和Activity可以通过getActivity 与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。
7. 如何处理运行时配置发生变化(Fragment 重叠问题)
在Activity的学习中我们都知道,当屏幕旋转时,是对屏幕上的视图进行了重绘。因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建。所以,不断的旋转就会不断的绘制,这是一种很消耗内存资源的操作,那么如何来进行优化?
通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建。默认的savedInstanceState会存储一些数据,包括Fragment的实例。所以,我们简单修改下代码,判断只有在savedInstanceState==null时,才进行创建Fragment实例。
8. onActivityResult()
Fragment 类提供了 startActivityForResult 方法用于 Activity 间的页面跳转和数据回传,其实内部也是调用了 Activity 的对应方法,但是在页面返回时 Fragment 没有提供 setResult 方法,但是可以通过拿宿主 Activity 实现。
但是仍要注意的一点是,嵌套的 Fragment 需要一级一级的分发。
9. 源码分析
9.1 FragmentManager
我们通过在 Activity 在 getSupportFragmentManager 获取的 FragmentManager 其实是FragmentManagerImpl,它也是抽象类 FragmentManager 的唯一实现类。
FragmentManagerImpl 里面有两个重要方法:
- beginTransaction()
public FragmentTransaction beginTransaction() {
return new BackStackRecord(this);
}
这里 BackStackRecord 也是抽象类 FragmentTransaction 的唯一实现。
- popBackStack()
public void popBackStack() {
enqueueAction(new PopBackStackState(null, -1, 0), false);
}
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
checkStateLoss();
}
synchronized (this) {
if (mPendingActions == null) {
mPendingActions = new ArrayList<>();
}
mPendingActions.add(action);
scheduleCommit();
}
}
9.2 BackStackRecord
final class BackStackRecord extends FragmentTransaction implements
FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
final FragmentManagerImpl mManager;
// 构造 Op 的标识位
static final int OP_NULL = 0;
static final int OP_ADD = 1;
static final int OP_REPLACE = 2;
static final int OP_REMOVE = 3;
//...
static final class Op {
//add、replace、remove 标志位
int cmd;
Fragment fragment;
//进入退出动画
int enterAnim;
int exitAnim;
int popEnterAnim;
int popExitAnim;
Op() {
}
Op(int cmd, Fragment fragment) {
this.cmd = cmd;
this.fragment = fragment;
}
}
//操作集合
ArrayList<Op> mOps = new ArrayList<>();
}
我们通过 FragmentTransaction add Fragment 时:
public FragmentTransaction add(int containerViewId, Fragment fragment){
doAddOp(containerViewId, fragment, null, OP_ADD);
return this;
}
private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
fragment.mFragmentManager = mManager;
addOp(new Op(opcmd, fragment));
}
void addOp(Op op) {
mOps.add(op);
op.enterAnim = mEnterAnim;
op.exitAnim = mExitAnim;
op.popEnterAnim = mPopEnterAnim;
op.popExitAnim = mPopExitAnim;
}