(一百九十)Android Jetpack 学习(五)—— fragment
片段Fragment 表示 FragmentActivity 中的行为或界面的一部分。您可以在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(这有点像可以在不同 Activity 中...
片段
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)。如下所示,您可以从
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()
回调。
更多推荐
所有评论(0)