Android开发---片段Fragment相关内容(片段管理、执行片段事务、事件传递、生命周期)概述

参考资料

概述

Fragment 表示 FragmentActivity 中的行为或界面的一部分。您可以在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。您可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除片段(这有点像可以在不同 Activity 中重复使用的“子 Activity”)。

片段必须始终托管在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。例如,当 Activity 暂停时,Activity 的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。不过,当 Activity 正在运行(处于已恢复生命周期状态)时,您可以独立操纵每个片段,如添加或移除片段。当执行此类片段事务时,您也可将其添加到由 Activity 管理的返回栈 — Activity 中的每个返回栈条目都是一条已发生片段事务的记录。借助返回栈,用户可以通过按返回按钮撤消片段事务(后退)。

本文介绍如何在开发应用时使用片段,包括如何在将片段添加到 Activity 返回栈时保持其状态、如何与 Activity 及 Activity 中的其他片段共享事件、如何为 Activity 的应用栏发挥作用等等。

创建片段

如果要创建片段,则必须创建Fragment的子类。可以实现片段的一些生命周期方法。
在这里插入图片描述

  • onCreate()
    系统会在创建片段时调用此方法。当片段经历暂停或停止状态继而恢复后,如果希望保留此片段的基本组件,则应在实现中将其初始化。
  • onCreateView()
    系统会在片段首次绘制其界面时调用此方法。如果要为片段绘制界面,从此方法中返回的View必须是片段布局的根视图。如果片段不需要返回界面,则可以返回null。
  • onPause()
    系统会将此方法作为用户离开片段的第一个信号(但不意味着片段会被销毁)进行调用。通常,应在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。

大多数应用应至少为每个片段实现这三个方法,但还应该使用几种其他回调方法来处理片段生命周期的各个阶段。

有几个现有的Fragment扩展类:

  • DialogFragment
    显示浮动对话框。
  • ListFragment
    显示由适配器(如 SimpleCursorAdapter)管理的一系列项目,类似于 ListActivity。该类提供几种管理列表视图的方法,如用于处理点击事件的 onListItemClick() 回调。(请注意,显示列表的首选方法是使用 RecyclerView,而非 ListView。)
  • PreferenceFragmentCompat
    以列表形式显示 Preference 对象的层次结构。此类用于为应用创建设置屏幕。

添加界面

片段通常用作Activity界面的一部分,并且会将自己的布局融入Activity。

如要为片段提供布局,必须实现onCreateView()回调方法。(注意:如果片段是 ListFragment 的子类,则默认实现会从 onCreateView() 返回一个 ListView,因此您无需实现它。

例如,以下 Fragment 子类从 example_fragment.xml 文件加载布局,onCreateView() 提供了一个 LayoutInflater 对象:

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);
    }
}

这将返回一个ExampleFragment的视图给视图组ViewGroup。

向Activity添加片段

通常,片段会向宿主Activity贡献一部分界面,作为整体视图层次结构的一部分嵌入到Activity中。可以通过两种方式向Activity布局添加片段:

  • 在Activity的布局文件内声明片段。
    可以将片段当做Activity的一个视图项,为其指定布局属性。
    <fragment>中的android:name属性指定要在布局中进行实例化的Fragment类。
    创建以上布局时,系统会将布局中指定的每个片段实例化,并为每个片段调用 onCreateView() 方法,以检索每个片段的布局。系统会直接插入片段返回的 View,从而代替 <fragment> 元素。
<?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>

注意:每个片段都需要唯一的标识符,重启Activity时,系统可以通过标识符来恢复片段(也可以使用标识符来捕获片段,从而执行某些事务,如将其移除)。
可以通过两种方式为片段提供ID:

  1. android:id属性可以提供唯一ID
  2. android:tag属性可以提供唯一字符串
  • 通过代码将片段添加到一个现有ViewGroup。
    在Activity运行期间,可以随时将片段添加到Activity布局中。如果要在Activity中执行片段事务(如添加、移除或替换片段),则必须使用FragmentTransaction中的API。
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

开启片段事务之后,可以在事务中,向ViewGroup添加一个片段。

ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();

针对片段的操作,需要通过事务进行提交,才可以生效。

管理片段

如果管理Activity中的片段,需使用 FragmentManager 。可以在Activity调用getSupportFragmentManager()。

可使用FragmentManager执行的操作包括:

  • 通过findFragmentById()(针对在Activity布局中提供界面的片段)或findFragmentByTag()(针对提供或不提供界面的片段)获取Activity中存在的片段。
  • 通过popBackStack()(模拟用户发出的返回命令)使片段从返回栈中弹出。
  • 通过addOnBackStackChangedListener()注册侦听返回栈变化的侦听器。

执行片段事务

在Activity中使用片段的一大优点是,可以通过片段执行添加、移除、替换等操作,相应用户交互。提交给Activity的每组更改都称为事务,并且可以使用 FragmentTransaction 中的API来执行一项事务。也可以将每个事务保存到由Activity管理的返回栈内,从而让用户能够回退片段更改(类似于回退Activity)。

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

每个事务都是要执行的一组更改。可以使用add()remove()replace()等方法,为给定事务设置要执行的更改。然后调用commit()方法提交更改到Activity。

不过,在调用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() 从返回栈检索片段。

如果向事务中添加了多个更改,并调用addToBackStack(),则调用commit()前应用的所有更改都将作为单一事务添加到返回栈,并且返回按钮会将其一并撤回。

向FragmentTransaction添加更改的顺序无关紧要,不过:

  • 必须最后调用commit()
  • 如果要向同一容器添加多个片段,则添加片段的顺序将决定他们在视图层次结构中出现的位置。

如果没有在执行删除片段的事务时调用addToBackStack(),则事务提交时该片段会被销毁。无法回退到该片段。

提示:对每个片段事务,都可以通过在提交之前调用setTransition()来应用过渡动画。

调用commit()不会立即执行事务,而是在Activity的界面线程(“主”线程)可执行该操作时,再安排该事务在线程上运行。如有必要,可以从界面线程调用executePendingTransactions(),立即执行提交事务。通常不必要这样,除非其他线程中的作业依赖该事务。

注意只能在Activity保存其状态(用户离开)之前提交事务如果您试图在该时间点后提交,则会引发异常。这是因为如需恢复 Activity,则提交后的状态可能会丢失。对于丢失提交无关紧要的情况,请使用 commitAllowingStateLoss()。

与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。若实现成功,listener成员会保留对 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。

处理片段生命周期

管理片段生命周期与管理Activity生命周期很相似。和Activity一样,片段也以三种状态存在:

  • 已恢复
    片段在运行中的Activity中可见。
  • 已暂停
    另一个Activity位于前台并具有焦点,但此片段所在的Activity仍然可见(前台Activity部分透明,或未覆盖整个屏幕)
  • 已停止
    片段不可见。宿主Activity已停止,或片段已从Activity中移除,但已添加到返回栈。已停止的片段仍处于活动状态(系统会保留所有状态和成员信息)。不过它对用户不再可见,并随Activity的终止而终止。
    在这里插入图片描述

与Activity生命周期协调一致

片段所在的Activity生命周期会直接影响片段生命周期。 其表现为,Activity 的每次生命周期回调都会引发每个片段的类似回调。例如,当 Activity 收到 onPause() 时,Activity 中的每个片段也会收到 onPause()。

不过,片段还有几个额外的生命周期回调,用于处理与 Activity 的唯一交互,从而执行构建和销毁片段界面等操作。这些额外的回调方法是:

  • onAttach()
    在片段已与Activity关联时进行调用(Activity传递到此方法内)
  • onCreateView()
    调用它可创建与片段关联的视图层次结构。
  • onActivityCreated()
    当 Activity 的 onCreate() 方法已返回时进行调用。
  • onDestroyView()
    在移除与片段关联的视图层次结构时进行调用。
  • onDetach()
    在取消片段与 Activity 的关联时进行调用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值