Fragment

[TOC]

本质上是view,但会是一个比较重量级的view(具有生命周期)

生命周期

注意点:

Fragment 的生命周期,除了第一次它创建或销毁之外,统统都是由 Activity 所驱动的

比如说同一个Activity中多个fragment的hide和show,只会调用到onHiddenChange(),并不会调用到onPause的生命周期!(但是用replace的话会重新创建fragment)

系统通过Fragment的setUserVisibleHint(boolean)来控制是否显示,故而若要在ViewPager中实现懒加载,最好将其实现在setUserVisibileHint()中。(ViewPager默认加载相邻的fragment,setOffscreenPageLimit只能设置大于等于1的数字)

下面说很有名的一张生命周期图:

complete_android_fragment_lifecycle

FragmentManager

  • 管理Activity中的所有的fragment
  • 所有fragment被放入一个栈中,每一个fragment都有一个FragmentState实例,相当于snapshot。
  • 内存重启时会把每个Fragment的state存储起来,最终存储到Activity的savedInstanceState中
  • 对于Fragment
    • getFragmentManager,获取父Fragment的FragmentManager(如果已是父Fragment,则会获得Activity的)
    • getChildFragmentManager,获取当前Fragment(可能是嵌套子Fragment)的FragmentManager

FragmentTransaction

1
2
3
4
5
Fragment fragment = new Fragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container,fragment)
.addToBackStack()
.commit();
  • addToBackStack表示将此事务压入栈中,在用户按下back键时会进行回退操作
    • 在上例中表示回到嵌入fragment之前的状态。
  • 执行hide() show(),会调用到onHiddenChanged(),不会调用onDestroyView()等。
  • add() replace()不要在同一阶级的FragmentManager里混搭使用
  • 此commit为异步操作,加入队列。否则请使用commit

与Activity的交互

Activity -> Fragment

  1. 使用argument进行传值

    1
    2
    3
    4
    5
    //in activity
    fragment.setArguments(Bundle);

    //in fragment
    getArgument();
  2. 使用fragmentmanager获取实例

    1
    2
    fragmentManager.findFragmnetById();
    fragmentManager.findFragmentByTag();

Fragment -> Activity

  1. 建立一个callback借口,由Activity来实现。这也是Android模版中的用法。

    也有比较暴力的方法是直接持有Activity引用的,这点一般不会出问题,但是从面向对象角度来说要尽量避免。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class DendiFragment extends Fragment{
    private Callbacks mCallbacks;

    public interface Callbacks{
    public void onItemClicked(Integer id);
    }

    @override
    public void onAttach(Activity activity){
    //TODO
    mCallbacks = (Callbacks)activity;
    }

    @override
    public void onDetach(){
    //TODO
    mCallbacks = null;
    }
    }
  2. getActivity()获得宿主Activity

    需要注意,如果当前Fragment已经onDetach(),则此方法会返回null。

    要避免异步任务中getActivity时返回空指针(同样也要注意如果使用引用可能造成的内存泄漏)

内存重启

系统回收会把Activity的状态保存下来,Activity的FragmentManager负责把Activity中的Fragment保存起来。

内存重启时FragmentManager会把每个Fragment的state存储起来,最终存储到Activity的savedInstanceState中。

保存的状态包括argument,故而getArgument()仍然可以拿到正确的数据。

另外需要注意内存重启情况下需要判断onCreate采取正常流程还是恢复流程(恢复流程的话fragment会储存在FragmentManager中,不需要重新new)

LifeCycle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
*ActivityA中嵌一个FragmentA,
*如果此时启动ActivityB,且ActivityA因为内存不足或其他原因被回收的时候,
*从B回到A,会回调这样的生命周期:
*/

ActivityB.onPause();

ActivityA.onCreate();
FragmentA.onAttach();
ActivityA.onAttachFragment();
FragmentA.onCreate();
FragmentA.onViewCreated();
ActivityA.onStart();
FragmentA.onStart();
ActivityA.onRestoreInstanceState();
ActivityA.onResume();
FragmentA.onResume();

ActivityB.onStop();
ActivityB.onDestroy();

/**
*ActivityA中嵌一个FragmentA,
*按下home,且ActivityA因为内存不足或其他原因被回收的时候,
*从屏幕回到A,会回调这样的声明周期:
*/

ActivityA.onCreate();
FragmentA.onAttach();
FragmentA.onCreate();
ActivityA.onStart();
FragmentA.onStart();
ActivityA.onResume();
FragmentA.onResume();

Fragment Overlap

当Fragment所在Activity被意外清理掉时,会从栈底向栈顶的顺序恢复fragments,并且全部都是以show()的方式,所以会看到界面重叠。(针对hide和show方式进行Fragment展示的场景,24版本以下在内存重启时没有保存mHidden字段)

也有可能是因为onCreate()中又执行了一遍。

1
2
3
4
5
6
7
8
9
10
@Override
protected void onCreate(Bundle savedInstanceState){
//todo

if(savedInstanceState != null){
//可以使用getFragments()获取栈内所有Fragment
getFragemntManager().beginTransaction().show(XX).hide(XX).commit();
}

}

Fragment State Loss

FragmentManager的每一个操作前(增加、移除、改变生命周期状态),都会调用一个方法来检查状态是否被保存过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 在onSaveInstanceState()
* android.support.v4.app.Fragment#onStop
*/
private void checkStateLoss(){
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);

}

如果是使用transaction的过程中报错,建议将事务放到onResumeFragment或者onPostResume中执行。

Extra

OnActivityResult

Fragment的onActivityResult只能接受fragment.startActivityForResult,同样的,fragment.startActivityForResult()返回的事件不会被该framgnet所属的activity的onActivityResult()接收到

Remove出栈

加入回退栈,不能使用remove,需要使用popBackStack()进行出栈

0%