Fragment 概要
Fragment表示 Activity 中的行为或用户界面部分。您可以将多个片段(片段就是指 Fragment )组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。
片段必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。
当您将片段作为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为 <fragment>
元素插入您的 Activity 布局中,即静态添加。或者通过将其添加到某个现有 ViewGroup,利用应用代码进行动态插入。不过,片段并非必须成为 Activity 布局的一部分;您还可以将没有自己 UI 的片段用作 Activity 的不可见工作线程。
Fragment 作用
Android 在 Android 3.0 (API 11)中引入了片段,主要是为了给更大的屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。由于平板屏幕比手机的屏幕大得多,也能显示更多的布局和组件。
例如,新闻应用可以使用一个片段在左侧显示文章列表,使用另一个片段在右侧显示文章 — 两个片段并排显示在一个 Activity 中,每个片段都具有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。 因此,用户不需要使用一个 Activity 来选择文章,然后使用另一个 Activity 来阅读文章,而是可以在同一个 Activity 内选择文章并进行阅读,如图 1 中的平板电脑布局所示。
在平板电脑尺寸的设备上运行时,该应用可以在 Activity A 中嵌入两个片段。 不过,在手机尺寸的屏幕上,没有足以储存两个片段的空间,因此Activity A 只包括用于显示文章列表的片段,当用户选择文章时,它会启动Activity B,其中包括用于阅读文章的第二个片段。 因此,应用可通过重复使用不同组合的片段来同时支持平板电脑和手机,如图 1 所示。
Fragment 生命周期
可以看到 Fragment 的生命周期和 Activity 很相似,只是多了一下几个方法:
onAttach() 在Fragment 和 Activity 建立关联是调用(Activity 传递到此方法内)
onCreateView() 当Fragment 创建视图时调用
onActivityCreated() 在相关联的 Activity 的 onCreate() 方法已返回时调用。
onDestroyView() 当Fragment中的视图被移除时调用
onDetach() 当Fragment 和 Activity 取消关联时调用。
可以看下几种操作情况下Fragment 的生命周期变化
管理 Fragment 生命周期和 Activity 生命周期很相似,同时 Activity 的生命周期对 Fragment 的生命周期也有一定的影响,如下图所示
用下图(来源)来表示 Activity 和 Fragment 的生命周期变化的先后过程是:
Fragment 生命周期与 Activity 生命周期的一个关键区别就在于,Fragment 的生命周期方法是由托管Activity而不是操作系统调用的。Activity 中生命周期方法都是 protected,而 Fragment 都是 public,也能印证了这一点,因为 Activity 需要调用 Fragment 那些方法并管理它。
加载 Fragment
- 静态加载
- 动态加载
- 静态加载 在 Activity 的布局文件内声明片段
下边代码是含有两个 Fragment 的 Activity 布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
其中 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()等。