片段
Fragment
表示FragmentActivity
中的行为或界面的一部分。您可以在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(这有点像可以在不同 Activity 中重复使用的“子 Activity”)。片段必须始终托管在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。例如,当 Activity 暂停时,Activity 的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除片段。当执行此类片段事务时,您也可将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。借助返回栈,用户可以通过按返回按钮撤消片段事务(后退)。
当您将片段作为 Activity 布局的一部分添加时,其位于 Activity 视图层次结构的某个
ViewGroup
中,并且片段会定义其自己的视图布局。您可以通过在 Activity 的布局文件中声明片段,将其作为<fragment>
元素插入您的 Activity 布局,或者通过将其添加到某个现有的ViewGroup
,利用应用代码将其插入布局。本文介绍如何在开发应用时使用片段,包括如何在将片段添加到 Activity 返回栈时保持其状态、如何与 Activity 及 Activity 中的其他片段共享事件、如何为 Activity 的应用栏发挥作用等等。
如需了解有关处理生命周期的信息(包括最佳实践的相关指导),请参阅以下资源:
创建片段
图 2. 片段的生命周期(当其 Activity 运行时)。
贴一下官方的fragment生命周期
执行片段事务
在 Activity 中使用片段的一大优点是,您可以通过片段执行添加、移除、替换以及其他操作,从而响应用户交互。提交给 Activity 的每组更改均称为事务,并且您可使用
FragmentTransaction
中的 API 来执行一项事务。您也可将每个事务保存到由 Activity 管理的返回栈内,从而让用户能够回退片段更改(类似于回退 Activity)。扫描二维码关注公众号,回复: 9544154 查看本文章如下所示,您可以从
FragmentManager
获取一个FragmentTransaction
实例:FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();每个事务都是您想要同时执行的一组更改。您可以使用
add()
、remove()
和replace()
等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到 Activity,您必须调用commit()
。不过,在调用
commit()
之前,您可能希望调用addToBackStack()
,以将事务添加到片段事务返回栈。该返回栈由 Activity 管理,允许用户通过按返回按钮返回上一片段状态。例如,以下示例说明如何将一个片段替换为另一个片段,以及如何在返回栈中保留先前的状态:
// Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getSupportFragmentManager().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 所标识的布局容器中的任何片段(如有)。通过调用addToBackStack()
,您可以将替换事务保存到返回栈,以便用户能够通过按返回按钮撤消事务并回退到上一片段。然后,
FragmentActivity
会自动通过onBackPressed()
从返回栈检索片段。如果您向事务添加多个更改(如又一个
add()
或remove()
),并调用addToBackStack()
,则调用commit()
前应用的所有更改都将作为单一事务添加到返回栈,并且返回按钮会将它们一并撤消。向
FragmentTransaction
添加更改的顺序无关紧要,不过:
- 您必须最后调用
commit()
。- 如果您要向同一容器添加多个片段,则您添加片段的顺序将决定它们在视图层次结构中出现的顺序。
如果您没有在执行删除片段的事务时调用
addToBackStack()
,则事务提交时该片段会被销毁,用户将无法回退到该片段。不过,如果您在删除片段时调用addToBackStack()
,则系统会停止该片段,并随后在用户回退时将其恢复。提示:对于每个片段事务,您都可通过在提交前调用
setTransition()
来应用过渡动画。调用
commit()
不会立即执行事务,而是在 Activity 的界面线程(“主”线程)可执行该操作时,再安排该事务在线程上运行。不过,如有必要,您也可以从界面线程调用executePendingTransactions()
,以立即执行commit()
提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务。注意:您只能在 Activity 保存其状态(当用户离开 Activity)之前使用
commit()
提交事务。如果您试图在该时间点后提交,则会引发异常。这是因为如需恢复 Activity,则提交后的状态可能会丢失。对于丢失提交无关紧要的情况,请使用commitAllowingStateLoss()
。
这边讲述了如何将之前的fragment恢复回来的方法。
与 Activity 通信
尽管
Fragment
作为独立于FragmentActivity
的对象实现,并且可在多个 Activity 内使用,但片段的给定实例会直接绑定到托管该片段的 Activity。具体而言,片段可通过
getActivity()
访问FragmentActivity
实例,并轻松执行在 Activity 布局中查找视图等任务:View listView = getActivity().findViewById(R.id.list);同样,您的 Activity 也可使用
findFragmentById()
或findFragmentByTag()
,通过从FragmentManager
获取对Fragment
的引用来调用片段中的方法。例如:ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.example_fragment);
创建 Activity 的事件回调
在某些情况下,您可能需使用片段来与 Activity 和/或 Activity 托管的其他片段共享事件或数据。如要共享数据,请依照 ViewModel 指南中“在片段之间共享数据”部分所述,创建共享的 ViewModel。如需传播无法使用 ViewModel 处理的事件,则可改为在片段内定义回调接口,并要求宿主 Activity 实现此接口。当 Activity 通过该接口收到回调时,可根据需要与布局中的其他片段共享这些信息。
例如,如果某个新闻应用的 Activity 有两个片段,其中一个用于显示文章列表(片段 A),另一个用于显示文章(片段 B),则片段 A 必须在列表项被选定后告知 Activity,以便它告知片段 B 显示该文章。在本例中,
OnArticleSelectedListener
接口在片段 A 内进行声明:public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }然后,该片段的宿主 Activity 会实现
OnArticleSelectedListener
接口并重写onArticleSelected()
,将来自片段 A 的事件通知片段 B。为确保宿主 Activity 实现此接口,片段 A 的onAttach()
回调方法(系统在向 Activity 添加片段时调用的方法)会通过转换传递到onAttach()
中的Activity
来实例化OnArticleSelectedListener
的实例:public static class FragmentA extends ListFragment { OnArticleSelectedListener listener; ... @Override public void onAttach(Context context) { super.onAttach(context); try { listener = (OnArticleSelectedListener) context; } catch (ClassCastException e) { throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener"); } } ... }如果 Activity 未实现接口,则片段会抛出
ClassCastException
。若实现成功,mListener
成员会保留对 Activity 的OnArticleSelectedListener
实现的引用,以便片段 A 可通过调用OnArticleSelectedListener
接口定义的方法与 Activity 共享事件。例如,如果片段 A 是ListFragment
的一个扩展,则用户每次点击列表项时,系统都会调用片段中的onListItemClick()
,然后该方法会通过调用onArticleSelected()
与 Activity 共享事件:public static class FragmentA extends ListFragment { OnArticleSelectedListener listener; ... @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 listener.onArticleSelected(noteUri); } ... }传递到
onListItemClick()
的id
参数是被点击项的行 ID,即 Activity(或其他片段)用来从应用的ContentProvider
获取文章的 ID。如需了解关于使用内容提供程序的详细信息,请参阅内容提供程序文档。
主要讲了fragment和activity通信可以通过回调的方法,fragment自己定义个接口,activity来实现,fragment可以通过attach时的context来获取对应回调的实现
处理片段生命周期
图 3. Activity 生命周期对片段生命周期的影响。
管理片段生命周期与管理 Activity 生命周期很相似。和 Activity 一样,片段也以三种状态存在:
已恢复
片段在运行中的 Activity 中可见。
已暂停
另一个 Activity 位于前台并具有焦点,但此片段所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
已停止
片段不可见。宿主 Activity 已停止,或片段已从 Activity 中移除,但已添加到返回栈。已停止的片段仍处于活动状态(系统会保留所有状态和成员信息)。不过,它对用户不再可见,并随 Activity 的终止而终止。
与 Activity 一样,您也可使用 onSaveInstanceState(Bundle)、ViewModel 和持久化本地存储的组合,在配置变更和进程终止后保留片段的界面状态。如要了解保留界面状态的更多信息,请参阅保存界面状态。
对于 Activity 生命周期与片段生命周期而言,二者最显著的差异是在其各自返回栈中的存储方式。默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈中(以便用户通过返回按钮回退到 Activity,详细介绍请参阅任务和返回栈)。不过,只有当您在移除片段的事务执行期间通过调用
addToBackStack()
显式请求保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。在其他方面,管理片段生命周期与管理 Activity 生命周期非常相似;对此,您可采取相同的做法。请参阅 Activity 生命周期指南和使用具有生命周期感知能力的组件处理生命周期,了解有关 Activity 生命周期及其管理措施的详情。
注意:如果您的
Fragment
中需要Context
对象,则可以调用getContext()
。但请注意,只有在该片段附加到 Activity 时才需调用getContext()
。如果尚未附加该片段,或者其在生命周期结束期间已分离,则getContext()
返回 null。
与 Activity 生命周期协调一致
片段所在 Activity 的生命周期会直接影响片段的生命周期,其表现为,Activity 的每次生命周期回调都会引发每个片段的类似回调。例如,当 Activity 收到
onPause()
时,Activity 中的每个片段也会收到onPause()
。不过,片段还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,从而执行构建和销毁片段界面等操作。这些额外的回调方法是:
在片段已与 Activity 关联时进行调用(
Activity
传递到此方法内)。调用它可创建与片段关联的视图层次结构。
当 Activity 的
onCreate()
方法已返回时进行调用。在移除与片段关联的视图层次结构时进行调用。
在取消片段与 Activity 的关联时进行调用。
图 3 所示为受宿主 Activity 影响的片段生命周期流。在该图中,您可以看到 Activity 的每个连续状态如何确定片段可收到的回调方法。例如,当 Activity 收到其
onCreate()
回调时,Activity 中的片段只会收到onActivityCreated()
回调。一旦 Activity 达到已恢复状态,您便可随意向 Activity 添加片段和移除其中的片段。因此,只有当 Activity 处于已恢复状态时,片段的生命周期才能独立变化。
不过,当 Activity 离开已恢复状态时,片段会在 Activity 的推动下再次经历其生命周期。
关键点:
如果尚未附加该片段,或者其在生命周期结束期间已分离,则 getContext()
返回 null。
在片段已与 Activity 关联时进行调用(Activity
传递到此方法内)。
疑点:activity的onCreate对应framgent的四个生命周期,调用顺序是怎么样的呢?
当 Activity 收到其 onCreate()
回调时,Activity 中的片段只会收到 onActivityCreated()
回调。