浅谈Fragment

原创
2015/11/08 16:05
阅读数 162

概述

你可以认为一个fragment相当于activity的一个模块部分,它可以有自己的生命周期,接收自己的输入事件,甚至可以在activity运行期间添加和删除(有点像可以在不同activity中重用的子activity)

你可以依赖Support V4 Library来使用Fragment,V4包提供了很多很有用的东西。V4包提供的Fragment可以向下兼容到Android 1.6 (API Level 4),如果你的最低API Level > = 11的话就没必要使用V4包了,官方在11之后就引入了Fragment类。大多数时候我们还是要使用V4的,谁叫它里面有个ViewPager呢!

Note:如果你想在API Level 11以下使用Fragment,那么你的Activity就应该继承FragmentActivity了,FragmentActivity是Support Library中用来兼容API Level 11以下在Activity中处理Fragment的特殊的Activity。如果你同时使用了V7 appcompat library,你应该继承AppCompatActivity,它是FragmentActivity的子类。

向Activity添加Fragment

你可以在XML布局文件中使用<fragment/>标签插入fragment

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

很方便,但是有局限性,如果我们想要使用BundleHeadLinesFragment传入一些值时,这种方式显得无能为力,你也可以在运行时向Activity添加Fragment,这个时候你需要知道这个类FragmentManagerFragmentManager提供了一些使你在运行时添加、移除和替换Fragment的方法,以便为用户提供一种动态体验,而且,如果你打算在运行周期内更改Fragment,就必须这样做。

你必须使用FragmentManager创建一个FragmentTransaction开启事务来执行添加、移除、替换以及使用其他事务API操作。如果你的Activity是FragmentActivity的子类,那么调用 getSupportFragmentManager() 以获取 FragmentManager,然后调用 beginTransaction() 创建 FragmentTransaction

另一个向Activity的XML布局文件插入Fragment的方式就是使用<FrameLayout/>标签,这种布局一次只会显示一个Fragment。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

在Activity中添加Fragment

public class MainActivity extends FragmentActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.news_articles);

            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, new HeadlinesFragment())
					.commit();

        }
    }

抽象层面上看,FrameLayout是作为一个容器来使用,可以为这个容器添加多个Fragment,最后添加的显示在最上层,注意add和replace的区别:

  • add是把一个fragment添加到一个容器中
  • replace是先remove容器里所有的fragment,然后再add目标fragment。

如果你希望允许用户向后导航,例如用户执行替换或移除Fragment等事务时,能够向后导航和“撤销”所做更改,事务提交之前调用addToBackStack()

Note: 当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁。 addToBackStack() 方法可接受可选的字符串参数,来为事务指定独一无二的名称。除非你打算使用 FragmentManager.BackStackEntry API 执行高级 Fragment 操作,否则无需使用此名称。

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

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

Fragment间的通信

为了能够重复利用Fragment UI组建,你最好将每一个Fragment设计成拥有自己的布局和行为的完全独立的模块化组件,你的Activity构建就是一场积木游戏。不同模块间少不了事件交流,例如点击一个评论,底栏输入框的hint就会显示“回复 @XXX”(加入你设计的二者不在一个Fragment)

为了让Fragment间通信,你可以下Fragment内部定义一个接口,在Activity实现它,不用麻烦的传入值,Fragment在生命周期方法onAttach()就可以捕获到它

public class HeadlinesFragment extends ListFragment {

   OnHeadlineSelectedListener mCallback;

    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mCallback = (OnHeadlineSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener");
        }

    }
}

向操作拦添加项目

您的片段可以通过实现 onCreateOptionsMenu() 向 Activity 的选项菜单(并因此向操作栏)贡献菜单项。不过,为了使此方法能够收到调用,您必须在 onCreate() 期间调用 setHasOptionsMenu(),以指示片段想要向选项菜单添加菜单项(否则,片段将不会收到对 onCreateOptionsMenu() 的调用)。

您之后从片段添加到选项菜单的任何菜单项都将追加到现有菜单项之后。 选定菜单项时,片段还会收到对 onOptionsItemSelected() 的回调。

您还可以通过调用 registerForContextMenu(),在片段布局中注册一个视图来提供上下文菜单。用户打开上下文菜单时,片段会收到对 onCreateContextMenu() 的调用。当用户选择某个菜单项时,片段会收到对 onContextItemSelected() 的调用。

Note: 尽管您的片段会收到与其添加的每个菜单项对应的菜单项选定回调,但当用户选择菜单项时,Activity 会首先收到相应的回调。 如果 Activity 对菜单项选定回调的实现不会处理选定的菜单项,则系统会将事件传递到片段的回调。 这适用于选项菜单和上下文菜单。

Fragment的生命周期

Fragment必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。 例如,当 Activity 暂停时,其中的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。

与Activity生命周期类似,Fragment也只能在三种状态下停留:

  • Resumed: Fragment在运行中的Activity可见
  • Paused: 另一个 Activity 位于前台并具有焦点,但此片段所在的 Activity 仍然可见(前台 Activity 部分透明,或未覆盖整个屏幕)。
  • Stopped: Fragment不可见。宿主 Activity 已停止,或片段已从 Activity 中删除,但已添加到返回栈。 停止片段仍然处于活动状态(系统会保留所有状态和成员信息)。 不过,它对用户不再可见,如果 Activity 被终止,它也会被终止。

同样与 Activity 一样,假使 Activity 的进程被终止,而您需要在重建 Activity 时恢复片段状态,您也可以使用 Bundle 保留片段的状态。您可以在片段的 onSaveInstanceState() 回调期间保存状态,并可在 onCreate()onCreateView()onActivityCreated() 期间恢复状态。

Activity 生命周期与片段生命周期之间的最显著差异在于它们在其各自返回栈中的存储方式。 默认情况下,Activity 停止时会被放入由系统管理的 Activity 返回栈。不过,仅当您在删除片段的事务执行期间通过调用 addToBackStack() 显式请求保存实例时,系统才会将片段放入由宿主 Activity 管理的返回栈。

Note: 如需 Fragment 内的某个 Context 对象,可以调用 getActivity()。但要注意,请仅在片段附加到 Activity 时调用 getActivity()。如果片段尚未附加,或在其生命周期结束期间分离,则 getActivity() 将返回 null。

展开阅读全文
打赏
0
2 收藏
分享
加载中
更多评论
打赏
0 评论
2 收藏
0
分享
返回顶部
顶部