Fragment 简单了解

Fragment的生命周期

答案参考自:

img

可以看到 Fragment 的生命周期和 Activity 很相似,只是多了一下几个方法:

  1. onAttach()

    在Fragment 和 Activity 建立关联时调用(Activity 传递到此方法内)。

  2. onCreateView()

    当Fragment 创建视图时调用。

  3. onActivityCreated()

    在相关联的 Activity 的 onCreate() 方法已返回时调用。

  4. onDestroyView()

    当Fragment中的视图被移除时调用。

  5. onDetach()

    当Fragment 和 Activity 取消关联时调用。

几种操作情况下Fragment 的生命周期变化

img

结合Activity的生命周期

管理 Fragment 生命周期和 Activity 生命周期很相似,同时 Activity 的生命周期对 Fragment 的生命周期也有一定的影响,如下图所示。

img

用下图(来源)来表示 Activity 和 Fragment 的生命周期变化的先后过程是:

img

Fragment 生命周期与 Activity 生命周期的一个关键区别就在于,Fragment 的生命周期方法是由托管Activity而不是操作系统调用的。Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印证了这一点,因为 Activity 需要调用 Fragment 那些方法并管理它。

ActivityFragment的通信方式

答案参考自:

将Fragment添加到Activity之后,Fragment必须与Activity交互信息,这就需要Fragment能获取它所在的Activity,Activity也能获取它所包含的任意的Fragment。

  • Fragment获取它所在的Activtiy

    调用Fragment的getActivity()方法即可返回它所在的Activity。

  • Activity获取它包含的Fragment

    调用Activity关联的FragmentManager的findFragmentById(int id)或findFragmentByTag(String tag)方法即可获取指定的Fragment。

除此之外,Fragment与Activity可能还需要相互传递数据,可按如下方式进行。

  • Activity向Fragment传递数据

    在Activity中创建Bundle数据包,并调用Fragment的setArguments(Bundle bundle)方法即可将Bundle数据包传给Fragment。

  • Fragment向Activity传递数据或Activity需要在Fragment运行中进行实时通信

    在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口,这样Fragment即可调用该回调方法将数据传给Activity。

使用 FragmentManager 还可以执行的操作包括:

  • 通过 findFragmentById 或 findFragmentByTag 获取 activity 中存在的 fragment 的实例
  • 通过 popBackStack (模拟用户点击返回按钮操作)将 fragment 从返回栈中弹出
  • 通过 addOnBackStackChangedListener() 注册一个监听返回栈改变的监听器
  • 像上边生成 fragmentTransaction 的方法,可以使用 fragmentManager 生成一个 fragmentTransaction 来执行某些事务,比如添加、替换、移除、addToBackStack()等。

Fragment之间如何进行通信

答案参考自:

Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。想象一下拆分视图 (list-detail) Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。

可以使用 ViewModel 对象解决这一常见的难点。这两个 fragment 可以使用其 activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

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
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

public void select(Item item) {
selected.setValue(item);
}

public LiveData<Item> getSelected() {
return selected;
}
}

public class ListFragment extends Fragment {
private SharedViewModel model;

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}

public class DetailFragment extends Fragment {

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}

请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。

此方法具有以下优势:

  • Activity 不需要执行任何操作,也不需要对此通信有任何了解。
  • 除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
  • 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。

为什么使用Fragment.setArguments(Bundle)传递参数

答案参考自:

当我们实例化自定义Fragment时,为什么官方推荐Fragment.setArguments(Bundle bundle)这种方式来传递参数,而不推荐通过构造方法直接来传递参数呢?

方式一:通过构造方法传递参数

在创建Fragment的时候,使用 MyFragment fragment = new MyFragment(parameter) 来传递参数。

1
2
3
4
5
public class MyFragment extends Fragment {
public MyFragment(Parameter p){
//将参数保存起来
}
}

方式二:通过Fragment.setArguments(Bundle)传递参数

在创建Fragment的时候,使用MyFragment fragment = MyFragment.newInstance(paramter) 来传递参数。

1
2
3
4
5
6
7
8
9
public class MyFragment extends Fragment {
public static MyFragment newInstance(Parameter p) {
MyFragment myFragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("someParameter", p);
myFragment.setArguments(args);
return myFragment;
}
}

结论

我们可以知道Activity重新创建时,会重新构建它所管理的Fragment,原先的Fragment的字段值将会全部丢失,但是通过 Fragment.setArguments(Bundle bundle)方法设置的bundle会保留下来。所以尽量使用Fragment.setArguments(Bundle bundle)方式来传递参数。

Fragment嵌套问题

答案参考自:

嵌套Fragments (Nested Fragments), 是在Fragment内部又添加Fragment。

使用时, 主要要依靠宿主Fragment的 getChildFragmentManager() 来获取FragmentManger。 虽然看起来和在activity中添加fragment差不多, 但因为fragment生命周期及管理恢复模式不同, 其中有一些需要特别注意的地方。

本文内容还包括了从Fragment迁移到v4.Fragment代码中需要改动的一些地方.

嵌套Fragments

嵌套Fragments 是Android 4.2 API 17 引入的.

目的: 进一步增强动态复用。

嵌套Fragment的动态添加

在宿主fragment里调用getChildFragmentManager()

即可用它来向这个fragment内部添加fragments。

1
2
3
Fragment videoFragment = new VideoPlayerFragment();
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
transaction.add(R.id.video_fragment, videoFragment).commit();

同样, 对于内部的fragment来说, getParentFragment() 方法可以获取到fragment的宿主fragment.

getChildFragmentManager() 和 getFragmentManager()

getChildFragmentManager()是fragment中的方法, 返回的是管理当前fragment内部子fragments的manager.

getFragmentManager()在activity和fragment中都有。

在activity中, 如果用的是v4 support库, 方法应该用getSupportFragmentManager(), 返回的是管理activity中fragments的manager.

在fragment中, 调用getFragmentManager(), 返回的是把自己加进来的那个manager.

也即, 如果fragment在activity中, fragment.getFragmentManager()得到的是activity中管理fragments的那个manager.
如果fragment是嵌套在另一个fragment中, fragment.getFragmentManager()得到的是它的parent的getChildFragmentManager()。

总结就是: getFragmentManager()是本级别管理者, getChildFragmentManager()是下一级别管理者。这实际上是一个树形管理结构。

TODO

  1. FragmentPageAdapterFragmentStatePageAdapter区别及使用场景

Fragment 简单了解
https://luoyuy.top/posts/219a53ef52d7/
作者
LuoYu-Ying
发布于
2023年3月3日
许可协议