(一百九十)Android Jetpack 学习(五)—— fragment

片段

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 实例:

KotlinJava

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每个事务都是您想要同时执行的一组更改。您可以使用 add()remove()replace() 等方法,为给定事务设置您想要执行的所有更改。然后,如要将事务应用到 Activity,您必须调用 commit()

不过,在调用 commit() 之前,您可能希望调用 addToBackStack(),以将事务添加到片段事务返回栈。该返回栈由 Activity 管理,允许用户通过按返回按钮返回上一片段状态。

例如,以下示例说明如何将一个片段替换为另一个片段,以及如何在返回栈中保留先前的状态:

KotlinJava

// 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 布局中查找视图等任务:

KotlinJava

View listView = getActivity().findViewById(R.id.list);

同样,您的 Activity 也可使用 findFragmentById()findFragmentByTag(),通过从 FragmentManager 获取对 Fragment 的引用来调用片段中的方法。例如:

KotlinJava

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 内进行声明:

KotlinJava

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 的实例:

KotlinJava

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 共享事件:

KotlinJava

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 的唯一交互,从而执行构建和销毁片段界面等操作。这些额外的回调方法是:

onAttach()

在片段已与 Activity 关联时进行调用(Activity 传递到此方法内)。

onCreateView()

调用它可创建与片段关联的视图层次结构。

onActivityCreated()

当 Activity 的 onCreate() 方法已返回时进行调用。

onDestroyView()

在移除与片段关联的视图层次结构时进行调用。

onDetach()

在取消片段与 Activity 的关联时进行调用。

图 3 所示为受宿主 Activity 影响的片段生命周期流。在该图中,您可以看到 Activity 的每个连续状态如何确定片段可收到的回调方法。例如,当 Activity 收到其 onCreate() 回调时,Activity 中的片段只会收到 onActivityCreated() 回调。

一旦 Activity 达到已恢复状态,您便可随意向 Activity 添加片段和移除其中的片段。因此,只有当 Activity 处于已恢复状态时,片段的生命周期才能独立变化。

不过,当 Activity 离开已恢复状态时,片段会在 Activity 的推动下再次经历其生命周期。

关键点:

如果尚未附加该片段,或者其在生命周期结束期间已分离,则 getContext() 返回 null。

onAttach()

在片段已与 Activity 关联时进行调用(Activity 传递到此方法内)。

疑点:activity的onCreate对应framgent的四个生命周期,调用顺序是怎么样的呢?

当 Activity 收到其 onCreate() 回调时,Activity 中的片段只会收到 onActivityCreated() 回调。

发布了198 篇原创文章 · 获赞 65 · 访问量 16万+

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/103002454
今日推荐