Fragment 是 Android 自从 3.0 之后新加入的一个组件,我相信很多人都已经听说过这个组件了,但这个组件到底是个什么,如何去使用他呢,且听我讲来。
以下部分资料来自官网(官网才是王道,其他都是浮云)
一、Fragment 要点
Fragment 作为 Activity 界面的一部分组成出现
可以在一个 Activity 中同时出现多个 Fragment,并且,一个 Fragment 亦可在多个 Activity 中使用。
在 Activity 运行过程中,可以添加、移除或者替换 Fragment(add()、remove()、replace())
Fragment 可以响应自己的输入事件,并且有自己的生命周期,当然,它们的生命周期直接被其所属的宿主 activity 的生命周期影响。
设计哲学
Android 在 3.0 中引入了 fragments 的概念, 主要目的是用在大屏幕设备上 -- 例如平板电脑上, 支持更加动态和灵活的 UI 设计。平板电脑的屏幕要比手机的大得多, 有更多的空间来放更多的 UI 组件, 并且这些组件之间会产生更多的交互。Fragment 允许这样的一种设计, 而不需要你亲自来管理 viewhierarchy 的复杂变化。 通过将 activity 的布局分散到 fragment 中, 你可以在运行时修改 activity 的外观, 并在由 activity 管理的 back stack 中保存那些变化.(http://developer.android.com/guide/topics/fundamentals/fragments.html)
例如, 一个新闻应用可以在屏幕左侧使用一个 fragment 来展示一个文章的列表, 然后在屏幕右侧使用另一个 fragment 来展示一篇文章 --2 个 fragment 并排显示在相同的一个 activity 中, 并且每一个 fragment 拥有它自己的一套生命周期回调方法, 并且处理它们自己的用户输入事件。 因此, 取代使用一个 activity 来选择一篇文章而另一个 activity 来阅读文章的方式, 用户可以在同一个 activity 中选择一篇文章并且阅读, 如图所示:
fragment 在你的应用中应当是一个模块化和可重用的组件. 即, 因为 fragment 定义了它自己的布局, 以及通过使用它自己的生命周期回调方法定义了它自己的行为, 你可以将 fragment 包含到多个 activity 中. 这点特别重要, 因为这允许你将你的用户体验适配到不同的屏幕尺寸. 举个例子, 你可能会仅当在屏幕尺寸足够大时, 在一个 activity 中包含多个 fragment, 并且, 当不属于这种情况时, 会启动另一个单独的, 使用不同 fragment 的 activity.
继续之前那个新闻的例子 -- 当运行在一个特别大的屏幕时 (例如平板电脑), 应用可以在 Activity A 中嵌入 2 个 fragment。然而, 在一个正常尺寸的屏幕(例如手机) 上, 没有足够的空间同时供 2 个 fragment 用, 因此, Activity A 会仅包含文章列表的 fragment, 而当用户选择一篇文章时, 它会启动 ActivityB, 它包含阅读文章的 fragment. 因此, 应用可以同时支持上图中的 2 种设计模式。
这时,大家应该知道 Fragment 到底是个什么东西了。
二、如何去使用一个 Fragment 呢
1、要创建一个 fragment, 必须创建一个 Fragment 的子类 (或者继承自一个已存在的它的子类). Fragment 类的代码看起来很像 Activity 。它包含了和 activity 类似的回调方法, 例如 onCreate()、 onStart()、onPause() 以及 onStop()。事实上, 如果你准备将一个现成的 Android 应用转换到使用 fragment, 可能只需简单的将代码从你的 activity 的回调方法分别移动到你的 fragment 的回调方法即可。
2、添加一个用户界面
fragment 通常用来作为一个 activity 的用户界面的一部分, 并将它的 layout 提供给 activity. 为了给一个 fragment 提供一 个 layout, 你必须实现 onCreateView() 回调方法, 当到了 fragment 绘制它自己的 layout 的时候, Android 系统调用它. 你的此方法的实现代码必须返回一个你的 fragment 的 layout 的根 view.
注意: 如果你的 fragment 是 ListFragment 的子类, 它的默认实现是返回从 onCreateView() 返回一个 ListView, 所以一般情况下不必实现它.
从 onCreateView() 返回的 View, 也可以从一个 layout 的 xml 资源文件中读取并生成. 为了帮助你这么做, onCreateView() 提供了一个 LayoutInflater 对象.
举个例子, 这里有一个 Fragment 的子类, 从文件 example_fragment.xml 加载了一个 layout:
传入 onCreateView() 的 container 参数是你的 fragmentlayout 将被插入的父 ViewGroup(来自 activity 的 layout) savedInstanceState 参数是一个 Bundle, 如果 fragment 是被恢复的, 它提供关于 fragment 的之前的实例的数据,
- 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);
- }
- }
- <?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>
然后你可以使用 add() 方法添加一个 fragment, 指定要添加的 fragment 和要插入的 view.
- FragmentManager fragmentManager = getFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
add() 的第一个参数是 fragment 要放入的 ViewGroup, 由 resource ID 指定, 第二个参数是需要添加的 fragment. 一旦用 FragmentTransaction 做了改变, 为了使改变生效, 必须调用 commit().
- ExampleFragment fragment = newExampleFragment();
- fragmentTransaction.add(R.id.fragment_container, fragment);
- fragmentTransaction.commit();
每一个事务都是同时要执行的一套变化. 可以在一个给定的事务中设置你想执行的所有变化, 使用诸如 add()、remove() 和 replace(). 然后, 要给 activity 应用事务, 必须调用 commit().
- FragmentManager fragmentManager = getFragmentManager();
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
在这个例子中, newFragment 替换了当前 layout 容器中的由 R.id.fragment_container 标识的 fragment. 通过调用 addToBackStack(), replace 事务被保存到 back stack, 因此用户可以回退事务, 并通过按下 BACK 按键带回前一个 fragment.
- // Create new fragment and transaction
- Fragment newFragment = newExampleFragment();
- FragmentTransaction transaction = getFragmentManager().beginTransaction();
- // Replace whatever is in thefragment_container view with this fragment,
- // and add the transaction to the backstack
- transaction.replace(R.id.fragment_container, newFragment);
- transaction.addToBackStack(null);
- // Commit the transaction
- transaction.commit();
同样地, activity 可以通过从 FragmentManager 获得一个到 Fragment 的引用来调用 fragment 中的方法, 使用 findFragmentById() 或 findFragmentByTag().
- View listView = getActivity().findViewById(R.id.list);
为 Activity 创建事件回调方法
- ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
然后 fragment 的宿主 activity 实现 OnArticleSelectedListener 接口, 并覆写 onArticleSelected() 来通知 fragment B, 从 fragment A 到来的事件. 为了确保宿主 activity 实现这个接口, fragment A 的 onAttach() 回调方法 (当添加 fragment 到 activity 时由系统调用) 通过将作为参数传入 onAttach() 的 Activity 做类型转换来实例化一个 OnArticleSelectedListener 实例.
- public static class FragmentA extends ListFragment {...
- // Container Activity must implement this interface
- public interface OnArticleSelectedListener {
- public void onArticleSelected(Uri articleUri);
- }...
- }
- public static class FragmentA extends ListFragment {
- OnArticleSelectedListener mListener;...@Override public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- mListener = (OnArticleSelectedListener) activity;
- } catch(ClassCastException e) {
- throw new ClassCastException(activity.toString() + " must implementOnArticleSelectedListener");
- }
- }
- ...
- }
传给 onListItemClick() 的 id 参数是被点击的项的行 ID, activity(或其他 fragment) 用来从应用的 ContentProvider 获取文章.
- public static class FragmentA extends ListFragment {
- OnArticleSelectedListener mListener;...@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
- mListener.onArticleSelected(noteUri);
- }
- ...
- }
和 activity 一样, 你可以使用 Bundle 保持 fragment 的状态, 万一 activity 的进程被干掉, 并且当 activity 被重新创建的时候, 你需要恢复 fragment 的状态时就可以用到. 你可以在 fragment 的 onSaveInstanceState() 期间保存状态, 并可以在 onCreate(), onCreateView() 或 onActivityCreated() 期间恢复它.
生命周期方面 activity 和 fragment 之间最重要的区别是各自如何在它的后台堆栈中储存. 在默认情况下, activity 在停止后, 它会被放到一个由系统管理的用于保存 activity 的后台堆栈.(因此用户可以使用 BACK 按键导航回退到它).
然而, 仅当你在一个事务期间移除 fragment 时, 显式调用 addToBackStack() 请求保存实例时, 才被放到一个由宿主 activity 管理的后台堆栈.
另外, 管理 fragment 的生命周期和管理 activity 生命周期非常类似. 因此, "managing the activitylifecycle" 中的相同实践也同样适用于 fragment. 你需要理解的是, activity 的生命如何影响 fragment 的生命.
与 activity 生命周期的协调工作
fragment 所生存的 activity 的生命周期, 直接影响 fragment 的生命周期, 每一个 activity 的生命周期的回调行为都会引起每一个 fragment 中类似的回调.
例如, 当 activity 接收到 onPause() 时, activity 中的每一个 fragment 都会接收到 onPause().
Fragment 有一些额外的生命周期回调方法, 那些是处理与 activity 的唯一的交互, 为了执行例如创建和销毁 fragment 的 UI 的动作. 这些额外的回调方法是:
onAttach()
当 fragment 被绑定到 activity 时被调用 (Activity 会被传入.).
onCreateView()
创建和 fragment 关联的 view hierarchy 时调用.
onActivityCreated()
当 activity 的 onCreate() 方法返回时被调用.
onDestroyView()
当和 fragment 关联的 view hierarchy 正在被移除时调用.
onDetach()
当 fragment 从 activity 解除关联时被调用.
fragment 生命周期的流程, 以及宿主 activity 对它的影响, 在图 3 中显示. 在这个图中, 可以看到 activity 依次的每个状态是如何决定 fragment 可能接收到的回调方法. 例如, 当 activity 接收到它的 onCreate(),activity 中的 fragment 接收到最多是 onActivityCreated().
一旦 activity 到达了 resumed 状态, 你可以自由地在 activity 添加和移除 fragment. 因此, 仅当 activity 处于 resumed 状态时, fragment 的生命周期才可以独立变化.
无论如何, 当 activity 离开 resumed 状态, fragment 再次被 activity 的推入它自己的生命周期过程.
来源: http://lib.csdn.net/article/android/41948