Fragment 表示 Activity 中的行为或用户界面部分。您能够将多个fragment组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个fragment。您能够将fragment视为 Activity 的模块化组成部分,它具备本身的生命周期,能接收本身的输入事件,而且您能够在 Activity 运行时添加或移除fragment(有点像您能够在不一样 Activity 中重复使用的“子 Activity”)。html
fragment必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的全部fragment也会暂停;当 Activity 被销毁时,全部fragment也会被销毁。 不过,当 Activity 正在运行(处于已恢复生命周期状态
)时,您能够独立操纵每一个fragment,如添加或移除它们。 当您执行此类fragment事务时,您也能够将其添加到由 Activity 管理的返回栈 — Activity 中的每一个返回栈条目都是一条已发生fragment事务的记录。 返回栈让用户能够经过按返回按钮撤消fragment事务(后退)。android
当您将fragment做为 Activity 布局的一部分添加时,它存在于 Activity 视图层次结构的某个 ViewGroup 内部,而且fragment会定义其本身的视图布局。您能够经过在 Activity 的布局文件中声明fragment,将其做为 <fragment>
元素插入您的 Activity 布局中,或者经过将其添加到某个现有 ViewGroup,利用应用代码进行插入。不过,fragment并不是必须成为 Activity 布局的一部分;您还能够将没有本身 UI 的fragment用做 Activity 的不可见工做线程。bash
本文描述如何在开发您的应用时使用fragment,包括将fragment添加到 Activity 返回栈时如何保持其状态、如何与 Activity 及 Activity 中的其余fragment共享事件、如何为 Activity 的操做栏发挥做用等等。app
Android 在 Android 3.0(API 级别 11)中引入了fragment,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。因为平板电脑的屏幕比手机屏幕大得多,所以可用于组合和交换 UI 组件的空间更大。利用fragment实现此类设计时,您无需管理对视图层次结构的复杂更改。 经过将 Activity 布局分红fragment,您能够在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。框架
例如,新闻应用可使用一个fragment在左侧显示文章列表,使用另外一个fragment在右侧显示文章 — 两个fragment并排显示在一个 Activity 中,每一个fragment都具备本身的一套生命周期回调方法,并各自处理本身的用户输入事件。 所以,用户不须要使用一个 Activity 来选择文章,而后使用另外一个 Activity 来阅读文章,而是能够在同一个 Activity 内选择文章并进行阅读,如图 1 中的平板电脑布局所示。ide
您应该将每一个fragment都设计为可重复使用的模块化 Activity 组件。也就是说,因为每一个fragment都会经过各自的生命周期回调来定义其本身的布局和行为,您能够将一个fragment加入多个 Activity,所以,您应该采用可复用式设计,避免直接从某个fragment直接操纵另外一个fragment。 这特别重要,由于模块化fragment让您能够经过更改fragment的组合方式来适应不一样的屏幕尺寸。 在设计可同时支持平板电脑和手机的应用时,您能够在不一样的布局配置中重复使用您的fragment,以根据可用的屏幕空间优化用户体验。 例如,在手机上,若是不能在同一 Activity 内储存多个fragment,可能必须利用单独fragment来实现单窗格 UI。模块化
有关由fragment定义的两个 UI 模块如何适应不一样设计的示例:经过组合成一个 Activity 来适应平板电脑设计,经过单独fragment来适应手机设计。布局
例如 ,仍然以新闻应用为例 , 在平板电脑尺寸的设备上运行时,该应用能够在 Activity A 中嵌入两个fragment。 不过,在手机尺寸的屏幕上,没有足以储存两个fragment的空间,所以Activity A 只包括用于显示文章列表的fragment,当用户选择文章时,它会启动Activity B,其中包括用于阅读文章的第二个fragment。 所以,应用可经过重复使用不一样组合的fragment来同时支持平板电脑和手机,如图 1 所示。优化
要想建立fragment,您必须建立 Fragment的子类(或已有其子类)。Fragment 类的代码与 Activity 很是类似。它包含与 Activity 相似的回调方法,如 onCreate()
、onStart()
、onPause()
和 onStop()
。实际上,若是您要将现有 Android 应用转换为使用fragment,可能只需将代码从 Activity 的回调方法移入fragment相应的回调方法中。下图是fragment生命周期方法:动画
一般,您至少应实现如下生命周期方法:
onCreate()
系统会在建立fragment时调用此方法。您应该在实现内初始化您想在fragment暂停或中止后恢复时保留的必需fragment组件。
onCreateView()
系统会在fragment首次绘制其用户界面时调用此方法。 要想为您的fragment绘制 UI,您今后方法中返回的 View必须是fragment布局的根视图。若是fragment未提供 UI,您能够返回 null。
onPause()
系统将此方法做为用户离开fragment的第一个信号(但并不老是意味着此fragment会被销毁)进行调用。 您一般应该在此方法内确认在当前用户会话结束后仍然有效的任何更改(由于用户可能不会返回)。
大多数应用都应该至少为每一个fragment实现这三个方法,但您还应该使用几种其余回调方法来处理fragment生命周期的各个阶段。 处理fragment生命周期
部分对全部生命周期回调方法作了更详尽的阐述。
您可能还想扩展几个子类,而不是 Fragment类:
DialogFragment
显示浮动对话框。使用此类建立对话框可有效地替代使用 Activity类中的对话框帮助程序方法,由于您能够将fragment对话框归入由 Activity 管理的fragment返回栈,从而使用户可以返回清除的fragment。
ListFragment
显示由适配器(如 SimpleCursorAdapter
)管理的一系列项目,相似于 ListActivity
。它提供了几种管理列表视图的方法,如用于处理点击事件的 onListItemClick()
回调。
PreferenceFragment
以列表形式显示 Preference
对象的层次结构,相似于 PreferenceActivity
。这在为您的应用建立“设置” Activity 时颇有用处。
fragment一般用做 Activity 用户界面的一部分,将其本身的布局融入 Activity。
要想为fragment提供布局,您必须实现 onCreateView()
回调方法,Android 系统会在fragment须要绘制其布局时调用该方法。您对此方法的实现返回的 View必须是fragment布局的根视图。
注:若是您的fragment是 ListFragment
的子类,则默认实现会从 onCreateView()
返回一个 ListView
,所以您无需实现它。
要想从 onCreateView()
返回布局,您能够经过 XML 中定义的布局资源来扩展布局。为帮助您执行此操做,onCreateView()
提供了一个 LayoutInflater
对象。
例如,如下这个 Fragment
子类从 example_fragment.xml
文件加载布局:
public static class ExampleFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.example_fragment, container, false);
}
}
复制代码
在上例中,R.layout.example_fragment
是对应用资源中保存的名为 example_fragment.xml
的布局资源的引用。
传递至 onCreateView()
的 container
参数是您的fragment布局将插入到的父 ViewGroup
(来自 Activity 的布局)。savedInstanceState
参数是在恢复fragment时,提供上一fragment实例相关数据的 Bundle
(处理fragment生命周期部分对恢复状态作了详细阐述)。
inflate()
方法带有三个参数:
ViewGroup
。传递 container
对系统向扩展布局的根视图(由其所属的父视图指定)应用布局参数具备重要意义;ViewGroup
(第二个参数)的布尔值。(在本例中,其值为 false,由于系统已经将扩展布局插入 container
— 传递 true 值会在最终布局中建立一个多余的视图组。)如今,您已经了解了如何建立提供布局的fragment。接下来,您须要将该fragment添加到您的 Activity 中。
一般,fragment向宿主 Activity 贡献一部分 UI,做为 Activity 整体视图层次结构的一部分嵌入到 Activity 中。能够经过两种方式向 Activity 布局添加fragment:
在 Activity 的布局文件内声明fragment
在本例中,您能够将fragment看成视图来为其指定布局属性。 例如,如下是一个具备两个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>
元素。
注:每一个fragment都须要一个惟一的标识符,重启 Activity 时,系统可使用该标识符来恢复fragment(您也可使用该标识符来捕获fragment以执行某些事务,如将其移除)。 能够经过三种方式为fragment提供 ID:
为 android:id
属性提供惟一 ID。
为 android:tag
属性提供惟一字符串。
若是您未给以上两个属性提供值,系统会使用容器视图的 ID。
或者经过代码方式将fragment添加到某个现有 ViewGroup
您能够在 Activity 运行期间随时将fragment添加到 Activity 布局中。您只需指定要将fragment放入哪一个 ViewGroup
。
要想在您的 Activity 中执行fragment事务(如添加、移除或替换fragment),您必须使用 FragmentTransaction
中的 API。您能够像下面这样从 Activity
获取一个 FragmentTransaction
实例:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
复制代码
而后,您可使用 add()
方法添加一个fragment,指定要添加的fragment以及将其插入哪一个视图。例如:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
复制代码
传递到 add()
的第一个参数是 ViewGroup
,即应该放置fragment的位置,由资源 ID 指定,第二个参数是要添加的fragment。
一旦您经过 FragmentTransaction
作出了更改,就必须调用 commit()
以使更改生效。
上例展现了如何向您的 Activity 添加fragment以提供 UI。不过,您还可使用fragment为 Activity 提供后台行为,而不显示额外 UI。(glide图片加载框架就使用了这种方式经过监听没有UI的fragment的生命周期来知道Activity的生命周期。)
要想添加没有 UI 的fragment,请使用 add(Fragment, String)
从 Activity 添加fragment(为fragment提供一个惟一的字符串“标记”,而不是视图 ID)。 这会添加fragment,但因为它并不与 Activity 布局中的视图关联,所以不会收到对onCreateView()
的调用。所以,您不须要实现该方法。
并不是只能为非 UI fragment提供字符串标记 — 您也能够为具备 UI 的fragment提供字符串标记 — 但若是fragment没有 UI,则字符串标记将是标识它的惟一方式。若是您想稍后从 Activity 中获取fragment,则须要使用 findFragmentByTag()
。
要想管理您的 Activity 中的fragment,您须要使用 FragmentManager
。要想获取它,请从您的 Activity 调用getFragmentManager()
。
您可使用 FragmentManager
执行的操做包括:
findFragmentById()
(对于在 Activity 布局中提供 UI 的fragment)或 findFragmentByTag()
(对于提供或不提供 UI 的fragment)获取 Activity 中存在的fragment。popBackStack()
(模拟用户发出的返回命令)将fragment从返回栈中弹出。addOnBackStackChangedListener()
注册一个侦听返回栈变化的侦听器。如需了解有关这些方法以及其余方法的详细信息,请参阅 FragmentManager 类文档。
如上文所示,您也可使用 FragmentManager
打开一个 FragmentTransaction
,经过它来执行某些事务,如添加和移除fragment。
在 Activity 中使用fragment的一大优势是,能够根据用户行为经过它们执行添加、移除、替换以及其余操做。 您提交给 Activity 的每组更改都称为事务,您可使用 [FragmentTransaction]
中的 API 来执行一项事务。您也能够将每一个事务保存到由 Activity 管理的返回栈内,从而让用户可以回退fragment更改(相似于回退 Activity)。
您能够像下面这样从 [FragmentManager]
获取一个 [FragmentTransaction]
实例:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
复制代码
每一个事务都是您想要同时执行的一组更改。您可使用 add()
、remove()
和 replace()
等方法为给定事务设置您想要执行的全部更改。而后,要想将事务应用到 Activity,您必须调用 commit()
。
不过,在您调用 commit()
以前,您可能想调用 addToBackStack()
,以将事务添加到fragment事务返回栈。 该返回栈由 Activity 管理,容许用户经过按返回按钮返回上一fragment状态。
例如,如下示例说明了如何将一个fragment替换成另外一个fragment,以及如何在返回栈中保留先前状态:
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
复制代码
在上例中,newFragment
会替换目前在 R.id.fragment_container
ID 所标识的布局容器中的任何fragment(若有)。经过调用 addToBackStack()
可将替换事务保存到返回栈,以便用户可以经过按返回按钮撤消事务并回退到上一fragment。
若是您向事务添加了多个更改(如又一个 add()
或 remove()
),而且调用了 addToBackStack()
,则在调用commit()
前应用的全部更改都将做为单一事务添加到返回栈,而且返回按钮会将它们一并撤消。
向 FragmentTransaction
添加更改的顺序可有可无,不过:
commit()
若是您没有在执行移除fragment的事务时调用 addToBackStack()
,则事务提交时该fragment会被销毁,用户将没法回退到该fragment。 不过,若是您在删除fragment时调用了 addToBackStack()
,则系统会中止该fragment,并在用户回退时将其恢复。
提示:对于每一个fragment事务,您均可以经过在提交前调用 setTransition()
来应用过渡动画。
调用 commit()
不会当即执行事务,而是在 Activity 的 UI 线程(“主”线程)能够执行该操做时再安排其在线程上运行。不过,若有必要,您也能够从 UI 线程调用 executePendingTransactions()
以当即执行 commit()
提交的事务。一般没必要这样作,除非其余线程中的做业依赖该事务。
注意:您只能在 Activity保存其状态(用户离开 Activity)以前使用 commit()
提交事务。若是您试图在该时间点后提交,则会引起异常。 这是由于如需恢复 Activity,则提交后的状态可能会丢失。 对于丢失提交可有可无的状况,请使用 commitAllowingStateLoss()
。
尽管 Fragment
是做为独立于 Activity
的对象实现,而且可在多个 Activity 内使用,但fragment的给定实例会直接绑定到包含它的 Activity。
具体地说,fragment能够经过 getActivity()
访问 Activity
实例,并轻松地执行在 Activity 布局中查找视图等任务。
View listView = getActivity().findViewById(R.id.list);
复制代码
一样地,您的 Activity 也可使用 findFragmentById()
或 findFragmentByTag()
,经过从 FragmentManager
获取对 Fragment
的引用来调用fragment中的方法。例如:
ExampleFragment fragment =
(ExampleFragment)getFragmentManager().findFragmentById(R.id.example_fragment);
复制代码
在某些状况下,您可能须要经过fragment与 Activity 共享事件。执行此操做的一个好方法是,在fragment内定义一个回调接口,并要求宿主 Activity 实现它。 当 Activity 经过该接口收到回调时,能够根据须要与布局中的其余fragment共享这些信息。
例如,若是一个新闻应用的 Activity 有两个fragment — 一个用于显示文章列表(fragment A),另外一个用于显示文章(fragment B)— 那么fragment A 必须在列表项被选定后告知 Activity,以便它告知fragment B 显示该文章。 在本例中,OnArticleSelectedListener
接口在fragment A 内声明:
public static class FragmentA extends ListFragment {
...
// Container Activity must implement this interface
public interface OnArticleSelectedListener {
public void onArticleSelected(Uri articleUri);
}
...
}
复制代码
而后,该fragment的宿主 Activity 会实现 OnArticleSelectedListener
接口并替代 onArticleSelected()
,未来自fragment A 的事件通知fragment B。为确保宿主 Activity 实现此接口,fragment A 的 onAttach()
回调方法(系统在向 Activity 添加fragment时调用的方法)会经过转换传递到 onAttach()
中的 Activity
来实例化 OnArticleSelectedListener
的实例:
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
...
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnArticleSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
}
}
...
}
复制代码
若是 Activity 未实现接口,则fragment会引起 ClassCastException
。实现时,mListener
成员会保留对 Activity 的 OnArticleSelectedListener
实现的引用,以便fragment A 能够经过调用 OnArticleSelectedListener
接口定义的方法与 Activity 共享事件。例如,若是fragment A 是 ListFragment
的一个扩展,则用户每次点击列表项时,系统都会调用fragment中的 onListItemClick()
,而后该方法会调用 onArticleSelected()
以与 Activity 共享事件:
public static class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } } 复制代码
传递到 onListItemClick()
的 id
参数是被点击项的行 ID,即 Activity(或其余fragment)用来从应用的 ContentProvider
获取文章的 ID。
您的fragment能够经过实现 onCreateOptionsMenu()
向 Activity 的选项菜单(并所以向应用栏)贡献菜单项。不过,为了使此方法可以收到调用,您必须在 onCreate()
期间调用 setHasOptionsMenu()
,以指示fragment想要向选项菜单添加菜单项(不然,fragment将不会收到对 onCreateOptionsMenu()
的调用)。
您以后从fragment添加到选项菜单的任何菜单项都将追加到现有菜单项以后。 选定菜单项时,fragment还会收到对 onOptionsItemSelected()
的回调。
您还能够经过调用 registerForContextMenu()
,在fragment布局中注册一个视图来提供上下文菜单。用户打开上下文菜单时,fragment会收到对 onCreateContextMenu()
的调用。当用户选择某个菜单项时,fragment会收到对 onContextItemSelected()
的调用。
注:尽管您的fragment会收到与其添加的每一个菜单项对应的菜单项选定回调,但当用户选择菜单项时,Activity 会首先收到相应的回调。 若是 Activity 对菜单项选定回调的实现不会处理选定的菜单项,则系统会将事件传递到fragment的回调。 这适用于选项菜单和上下文菜单。
Activity 生命周期对fragment生命周期的影响。
管理fragment生命周期与管理 Activity 生命周期很类似。和 Activity 同样,fragment也以三种状态存在:
继续 fragment在运行中的 Activity 中可见。
暂停 另外一个 Activity 位于前台并具备焦点,但此fragment所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
中止 fragment不可见。宿主 Activity 已中止,或fragment已从 Activity 中移除,但已添加到返回栈。 中止fragment仍然处于活动状态(系统会保留全部状态和成员信息)。 不过,它对用户再也不可见,若是 Activity 被终止,它也会被终止。
一样与 Activity 同样,假使 Activity 的进程被终止,而您须要在重建 Activity 时恢复fragment状态,您也可使用 Bundle
保留fragment的状态。您能够在fragment的 onSaveInstanceState()
回调期间保存状态,并可在onCreate()
、onCreateView()
或 onActivityCreated()
期间恢复状态。
Activity 生命周期与fragment生命周期之间的最显著差别在于它们在其各自返回栈中的存储方式。 默认状况下,Activity 中止时会被放入由系统管理的 Activity 返回栈(以便用户经过返回按钮回退到 Activity)。不过,仅当您在移除fragment的事务执行期间经过调用addToBackStack()
显式请求保存实例时,系统才会将fragment放入由宿主 Activity 管理的返回栈。
在其余方面,管理fragment生命周期与管理 Activity 生命周期很是类似。 所以,管理 Activity 生命周期的作法一样适用于fragment。 但您还须要了解 Activity 的生命周期对fragment生命周期的影响。
注意:如需 Fragment
内的某个 Context
对象,能够调用 getActivity()
。但要注意,请仅在fragment附加到 Activity 时调用 getActivity()
。若是fragment还没有附加,或在其生命周期结束期间分离,则 getActivity()
将返回 null。
fragment所在的 Activity 的生命周期会直接影响fragment的生命周期,其表现为,Activity 的每次生命周期回调都会引起每一个fragment的相似回调。 例如,当 Activity 收到 onPause()
时,Activity 中的每一个fragment也会收到 onPause()
。
不过,fragment还有几个额外的生命周期回调,用于处理与 Activity 的惟一交互,以执行构建和销毁fragment UI 等操做。 这些额外的回调方法是:
onAttach()
在fragment已与 Activity 关联时调用(Activity
传递到此方法内)。
onCreateView()
调用它可建立与fragment关联的视图层次结构。
onActivityCreated()
在 Activity 的 onCreate()
方法已返回时调用。
onDestroyView()
在移除与fragment关联的视图层次结构时调用。
onDetach()
在取消fragment与 Activity 的关联时调用。
图 3 图示说明了受其宿主 Activity 影响的fragment生命周期流。在该图中,您能够看到 Activity 的每一个连续状态如何决定fragment能够收到的回调方法。 例如,当 Activity 收到其 onCreate()
回调时,Activity 中的fragment只会收到onActivityCreated()
回调。
一旦 Activity 达到恢复状态,您就能够随意向 Activity 添加fragment和移除其中的fragment。 所以,只有当 Activity 处于恢复状态时,fragment的生命周期才能独立变化。
不过,当 Activity 离开恢复状态时,fragment会在 Activity 的推进下再次经历其生命周期。