Fragment 概要
片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为 <fragment> 元素插入您的 Activity 布局中,即静态添加。或者通过将其添加到某个现有 ViewGroup,利用应用代码进行动态插入。
不过,片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的片段用作 Activity 的不可见工作线程。
Fragment 生命周期
可以看到 Fragment 的生命周期和 Activity 很相似,只是多了一下几个方法:
onAttach() 在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)
onCreateView() 当Fragment 创建视图时调用
onActivityCreated() 在相关联的 Activity 的 onCreate() 方法已返回时调用。
onDestroyView() 当Fragment中的视图被移除时调用
onDetach() 当Fragment 和 Activity 取消关联时调用。
可以看下几种操作情况下Fragment 的生命周期变化
管理 Fragment 生命周期和 Activity 生命周期很相似,同时 Activity 的生命周期对 Fragment 的生命周期也有一定的影响,如下图所示
Activity 生命周期对片段生命周期的影响
用下图来表示 Activity 和 Fragment 的生命周期变化的先后过程是:
Activity 和 Fragment 生命周期执行过程
Fragment 生命周期与 Activity 生命周期的一个关键区别就在于,Fragment 的生命周期方法是由托管Activity而不是操作系统调用的。Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印证了这一点,因为 Activity 需要调用 Fragment 那些方法并管理它。
加载 Fragment
- 静态加载
- 动态加载
-
静态加载 在 Activity 的布局文件内声明片段
下边代码是含有两个 Fragment 的 Activity 布局
其中 fragment 中的 android:name 属性要指定 fragment 对应的具体包名路径,当系统创建此 Activity 布局时,会实例化在布局中指定的每个 fragment,并为每个 fragment 调用 onCreateView()方法,以检索每个 fragment 的布局。系统会直接插入 fragment 返回的 View 来替代 fragment 元素。
并且在 Activity 活动里可以直接使用 findViewById() 方法获取 fragment 对应布局里的控件。同样在 fragment 里可以直接使用 getActivity()方法获得绑定的主 Activity 实例,并调用 Activity 里的方法或其他 fragment 实例。
-
动态加载 通过编程方式将 fragment 添加到某个activity布局里现有的 ViewGroup (例如 LinearLayout 或 FrameLayout)里。
要想在 Avtivity 中执行 Fragment 事务 (如添加、删除或替换 Fragment),必须使用 FragmentTransaction 中的 API。可以使用下面这样从 Activity 中获取一个 FragmentTransaction。FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后可以使用 add()方法添加一个 fragment ,指定要添加的 fragment 和插入到哪个视图。例如
ExampleFragment exampleFragment = new ExampleFragment();
fragmentTransaction.add(R.id.frame_layout,exampleFragment);
fragmentTransaction.commit();
add 方法中第一个参数是一个activity 对应布局文件中的 ViewGroup,即应该放置 fragment 的位置,由资源 ID 指定,第二个参数是加入的 fragment ,一旦通过 fragmentTransaction 做了更改,最后必须使用 commit 方法以使更改生效。
添加没有 UI 的 fragment
上边展示了如何向您的 Activity 添加 fragment 以提供 UI。不过,还可以使用 fragment 为 Activity 提供后台行为,而不显示额外 UI。
要想添加没有 UI 的片段,请使用 add(Fragment, String) 从 Activity 添加片段(为片段提供一个唯一的字符串“标记”,而不是视图 ID)。 这会添加片段,但由于它并不与 Activity 布局中的视图关联,因此不会收到对onCreateView() 的调用。因此,不需要实现该方法。
执行 Fragment 事务
在 Activity 中使用 Fragment 可以很方便的进行添加 add、替换 replace、移除 remove 等操作,这样提交给 Activity 的每组更改都可以称为事务。像上边动态添加 fragment 那样,使用 FragmentTransaction 里的 API 就可以执行一项事务。同时也可以将此事务保存到 Activity 管理的返回栈中,从而用户可以回退到 fragment 改变之前的状态(类似于 activity 回退到上一个页面)。
下边的例子演示了如何替换一个 fragment,并把替换之前的状态保存到 Activity 的返回栈中。
Fragment newFragment = new ExampleFragment();
FragmentTransaction mTransaction = getFragmentManager().beginTransaction();
//用新的 fragment 替换原来fragment 所在位置的布局,并且把此事务添加到返回栈中。
mTransaction.replace(R.id.frame_layout,newFragment);
mTransaction.addToBackStack(null);
mTransaction.commit();
如果在执行移除 fragment 的事务时没有调用 addToBackStack(),则事务提交时该 fragment 会被销毁,用户将无法回退到该 fragment 。 不过,如果在删除 fragment 时调用了 addToBackStack(),则系统会
停止
该 fragment,并在用户按返回键时将其恢复。
Fragment 与 Activity 通信
上边说过,在 fragment 中可以调用 getActivity() 获取 activity 的实例并调用 activity 里的方法和布局,同样在 activity 里也可以通过 findFragmentById()(对于在 activity 提供 fragment 布局的) 或 findFragmentByTag() (对于在 activity 提供或者不提供 fragment 布局的)方法获取 fragment 的实例,例如在 activity 中从 FragmentManager 获取对 Fragment 的引用来调用 fragment 中的方法:
Fragment fragment = getFragmentManager.findFragmentById(R.id.fragment_container);
使用 FragmentManager 还可以执行的操作包括:
-
通过 findFragmentById 或 findFragmentByTag 获取 activity 中存在的 fragment 的实例
-
通过 popBackStack (
模拟用户点击返回按钮操作
)将 fragment 从返回栈中弹出
-
通过 addOnBackStackChangedListener() 注册一个监听返回栈改变的监听器
-
像上边生成 fragmentTransaction 的方法,可以使用 fragmentManager 生成一个 fragmentTransaction 来执行某些事务,比如添加、替换、移除、addToBackStack()等。
replace 方法fragment生命周期
fragment 使用replace 来切换的时候使用 显示的fragment会onAttach到onResume ,被切换的fragment会onPause 到 onDetach
hide&show 方法fragment生命周期 onHiddenChanged
hide &show 方法不触发Fragment生命周期,只影响它的显示
而是走onHiddenChanged方法:
当Fragment隐藏时,该方法会调用传入参数为true表示该Fragment被隐藏了,当Fragment调用了show方法后,该方法传入的参数为false,表示该Fragment正在显示!
所以,如果使用hide/show方法来控制Fragment的使用时,原本需要在onResume以及onPause方法做的事情就可以迁移到 onHiddenChanged时进行管理,如:
if (hidden) {// 不在最前端界面显示
} else {// 重新显示到最前端
}
这样就能完美实现当前Fragment在隐藏和显示时分别需要做的事了
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) { //不在最前端界面显示
mVideoView.pause();
} else { //重新显示到最前端
mVideoView.start();
}
}