前言

Fragment,中文译为碎片。被设计为在一定程度上可代替activity部分功能的组件(但是必须依赖于某一activity)。较之于Activity来说,Fragment更为轻量级,也可以适应不同平板和手机的分辨率,实现响应式UI。这也是谷歌比较推崇的一种方式->一个Activity对应多个fragment来构建app。 虽然现在fragment存在着许多的坑,但是在一定程度上是一种趋势吧(PS:虽然现在有很多开发者表示自己不用fragment, 因为坑太多)。

在这里就对fragment的相关知识点进行一个简单的总结及梳理。

生命周期

生命周期

这是fragment的一个生命周期图,从这里我们可以看出fragment的生命周期是和activity相对应的,同时,一个fragment必须依赖于一个activity(多对一或一对一),当宿主activity被销毁时,所有依赖于此activity的fragment都会被销毁。

1. onAttach(): 当fragment被添加到activity中时调用此方法,并且只调用一次。
2. onCreate(): 创建fragment时调用,也只调用一次
3. onCreateView(): 每次创建都会绘制fragment的View组件
4. onActivityCreated(): 当fragment所在的宿主activity创建完成后会调用,也就是响应activity的onCreate方法。
5. onStart(): 每次启动fragment的时候调用
6. onResume(): 恢复fragment时被调用,调用完onStart()方法后一定会调用此方法。
7. onPause(): 暂停fragment
8. onStop(): 停止fragment
9. onDestroyView(): 销毁fragment包含的view组件
10. onDestroy(): 销毁fragment时被调用
11. onDetach(): 对应onAttach()方法,用于解除联系。当fragment从Activity中删除时调用,也只调用一次。

使用方法

静态加载

类似在xml中填写控件的方法,(一般多用v4包中的fragment,因为可向下支持)。里面填写属性,一个重要的属性就是name,用于指定自己的fragment

动态加载

通过在代码中创建对应的实例来实现加载,利用add或replace方法来进行替换。

1
2
3
4
fragmentManager.beginTransaction()
.addToBackStack("fragmentOne")
.replace(R.id.container, new FragmentOne())
.commit();

优化

在我们创建fragment的时候,有一件事情是需要注意的,那就是资源的重复利用。
在fragment的onCreatView()方法中,一般是这样创建的:

1
2
3
4
5
6
7
8
9
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.xml_fragmentone, container, false);

// init
init();
return rootView;
}

这种做法是最简单的做法,但是其实并不好,没有考虑到效率的问题,因为有的时候view已经被初始化过了,还没被回收。每次onCreateView方法都进行rootView的初始化并不好。建议采用下面的这种优化方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

if (rootView == null) {
rootView = inflater.inflate(R.layout.xml_fragmentone, container, false);

// init
init();
}

ViewGroup parent = (ViewGroup) rootView.getParent();

if (parent != null) {
parent.removeView(rootView);
}
return rootView;
}

先进行检测是否初始化过了,如果没有初始化过就可以直接初始化。如果已经初始化了,就需要检测parent中有没有rootView,如果有就需要移除之后重新添加。

常用方法

创建完fragment之后就要考虑到相关的使用了,fragment主要的操作都是FragmentTransaction(事务)完成的。

  • FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
  • 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上并显示。
  • transatcion.commit() //提交一个事务

注意:

  • 如果遇到activity状态不一致State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。
  • 一个FragmentTransaction只能被提交一次。,因此,可采用fragmentManager.beginTransaction()开启事务

使用示例

1
2
3
4
5
6
7
8
9
// replace
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new FragmentOne())
.commit();

// add
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new FragmentOne())
.commit();

回退栈

当使用replace方法时,如果不对当前的fragment或者container添加回退栈操作 fragmentManager.beginTransaction().addToBackStack(String)的话,这个fragment实例会被直接销毁,当添加入回退栈后,在replace完成后的fragment中使用fragmentManager.popBackStack()可退回到上一个fragment。

传递数据

传递数据分两个,一个是activity向fragment中传递。第二个是fragment向activity中传递。

Activity向Fragment传递数据

使用setArgument方法,利用Bundle传递数据

Fragment向Activity中传递数据

这种传递的话可以分为两类,第一类是当Fragment创建完成之后给Activity一个返回值,类似于onActivityResult这个方法。这个就需要在onAttach(Activity activity)中进行编写,该activity参数就是fragment要绑定的activity的对象。同时,再在Fragment的onActivityResult中编写相关逻辑就可以了。

第二类传递的方式是:Fragment中产生某个事件时,需要把相关信息传递给activity处理或者是传值。这个时候有两种解决方法。一个是接口,一个就是eventBus或者RxBus,观察者模式。至于观察者模式如何使用,请看我以前的博客: RxJava使用示例(一): 实现Rxbus代替eventbus

最后的话

请注意,为了解耦以及fragment本身设计的限制,如果能将事件交给宿主activity处理的话就交给Activity处理。例如在本fragment中操作其他fragment;处理数据等等。

同时,在使用fragment时,一定要注意它的生命周期的管理以及相关的操作。