写点什么

android 之 Fragment(官网资料翻译)

用户头像
Android架构
关注
发布于: 2021 年 11 月 07 日

因为你可以将一个 fragment 对话框合并到 activity 管理的 fragment back stack 中,允许用户返回到一个之前曾被摒弃的 fragment.


  • **ListFragment


**显示一个由一个 adapter(例如 SimpleCursorAdapter)管理的项目的列表, 类似于 ListActivity.


它提供一些方法来管理一个 list view, 例如 onListItemClick()回调来处理点击事件.


  • PreferenceFragment


显示一个 Preference 对象的层次结构的列表, 类似于 PreferenceActivity.?


这在为你的应用创建一个"设置"activity 时有用处.

添加一个用户界面? ?

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:


[java]? view plain copy


  1. public?static?class?ExampleFragment?extends?Fragment?{??

  2. ????@Override??

  3. ????public?View?onCreateView(LayoutInflater?inflater,?ViewGroup?container,??

  4. ?????????????????????????????Bundle?savedInstanceState)?{??

  5. ????????//?Inflate?the?layout?for?this?fragment??

  6. ????????return?inflater.inflate(R.layout.example_fragment,?container,?false);??

  7. ????}??

  8. }??


传入 onCreateView()的 container 参数是你的 fragmentlayout 将被插入的父 ViewGroup(来自 activity 的 layout)? savedInstanceState 参数是一个 Bundle, 如果 fragment 是被恢复的,它提供关于 fragment 的之前的实例的数据,


inflate() 方法有 3 个参数:


  • 想要加载的 layout 的 resource ID.

  • 加载的 layout 的父 ViewGroup.


传入 container 是很重要的, 目的是为了让系统接受所要加载的 layout 的根 view 的 layout 参数,


由它将挂靠的父 view 指定.


  • 布尔值指示在加载期间, 展开的 layout 是否应当附着到 ViewGroup (第二个参数).


(在这个例子中, 指定了 false, 因为系统已经把展开的 layout 插入到 container –传入 true 会在最后的 layout 中创建一个多余的 view group.)

将 fragment 添加到 activity

通常地, fragment 为宿主 activity 提供 UI 的一部分, 被作为 activity 的整个 viewhierarchy 的一部分被嵌入. 有 2 种方法你可以添加一个 fragment 到 activity layout:


在 activity 的 layout 文件中声明 fragment


在这种情况下,你可以像为 View 一样, 为 fragment 指定 layout 属性.例子是一个有 2 个 fragment 的 activity 的 layout:


[html]? view plain copy


  1. <?xml?version="1.0"?encoding="utf-8"?>??

  2. <LinearLayout?xmlns:android="http://schemas.android.com/apk/res/android"??

  3. ????android:orientation="horizontal"??

  4. ????android:layout_width="match_parent"??

  5. ????android:layout_height="match_parent">??

  6. ????<fragment?android:name="com.example.news.ArticleListFragment"??

  7. ????????????android:id="@+id/list"??

  8. ????????????android:layout_weight="1"??

  9. ????????????android:layout_width="0dp"??

  10. ????????????android:layout_height="match_parent"?/>??

  11. ????<fragment?android:name="com.example.news.ArticleReaderFragment"??

  12. ????????????android:id="@+id/viewer"??

  13. ????????????android:layout_weight="2"??

  14. ????????????android:layout_width="0dp"??

  15. ????????????android:layout_height="match_parent"?/>??

  16. </LinearLayout>??


<fragment> 中的 android:name 属性指定了在 layout 中实例化的 Fragment 类.


当系统创建这个 activity layout 时,它实例化每一个在 layout 中指定的 fragment,并调用每一个上的 onCreateView()方法,来获取每一个 fragment 的 layout.系统将从 fragment 返回的 View 直接插入到<fragment>元素所在的地方.?


注意 : ?每一个 fragment 都需要一个唯一的标识,如果 activity 重启,系统可以用来恢复 fragment(并且你也可以用来捕获 fragment 来处理事务,例如移除它.)???


有 3 种方法来为一个 fragment 提供一个标识:


  • 为 android:id 属性提供一个唯一 ID.

  • 为 android:tag 属性提供一个唯一字符串.

  • 如果以上 2 个你都没有提供, 系统使用容器 view 的 ID.

撰写代码将 fragment 添加到一个已存在的 ViewGroup.

当 activity 运行的任何时候, 都可以将 fragment 添加到 activity layout.只需简单的指定一个需要放置 fragment 的 ViewGroup.为了在你的 activity 中操作 fragment 事务(例如添加,移除,或代替一个 fragment),必须使用来自 FragmentTransaction 的 API.


可以按如下方法,从你的 Activity 取得一个 FragmentTransaction 的实例:


[java]? view plain copy


  1. FragmentManager?fragmentManager?=getFragmentManager();??

  2. FragmentTransaction?fragmentTransaction?=fragmentManager.beginTransaction();??


然后你可以使用 add() 方法添加一个 fragment, 指定要添加的 fragment 和要插入的 view.


[java]? view plain copy


  1. ExampleFragment?fragment?=?newExampleFragment();??

  2. fragmentTransaction.add(R.id.fragment_container,fragment);??

  3. fragmentTransaction.commit();??


add()的第一个参数是 fragment 要放入的 ViewGroup, 由 resource ID 指定,第二个参数是需要添加的 fragment.一旦用 FragmentTransaction 做了改变,为了使改变生效,必须调用 commit().

添加一个无 UI 的 fragment

之前的例子展示了对 UI 的支持, 如何将一个 fragment 添加到 activity.然而,也可以使用 fragment 来为 activity 提供后台行为而不用展现额外的 UI.


要添加一个无 UI 的 fragment, 需要从 activity 使用 add(Fragment, String)来添加 fragment (为 fragment 提供一个唯一的字符串"tag", 而不是一个 view ID).这么做添加了 fragment,但因为它没有关联到一个 activity layout 中的一个 view


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


, 所以不会接收到 onCreateView()调用.因此不必实现此方法.


为 fragment 提供一个字符串 tag 并不是专门针对无 UI 的 fragment 的–也可以提供字符串 tag 给有 UI 的 fragment–但是如果 fragment 没有 UI,那么这个 tag 是仅有的标识它的途径.如果随后你想从 activity 获取这个 fragment, 需要使用 findFragmentByTag().


管理 Fragment




要在 activity 中管理 fragment,需要使用 FragmentManager. 通过调用 activity 的 getFragmentManager()取得它的实例.


可以通过 FragmentManager 做一些事情, 包括:?


  • 使用 findFragmentById()(用于在 activity layout 中提供一个 UI 的 fragment)或 findFragmentByTag()(适用于有或没有 UI 的 fragment)获取 activity 中存在的 fragment

  • 将 fragment 从后台堆栈中弹出, 使用 popBackStack() (模拟用户按下 BACK 命令).

  • 使用 addOnBackStackChangeListener()注册一个监听后台堆栈变化的 listener.


处理 Fragment 事务




关于在 activity 中使用 fragment 的很强的一个特性是:根据用户的交互情况,对 fragment 进行添加,移除,替换,以及执行其他动作.提交给 activity 的每一套变化被称为一个事务,可以使用在 FragmentTransaction 中的 API 处理.我们也可以保存每一个事务到一个 activity 管理的 backstack,允许用户经由 fragment 的变化往回导航(类似于通过 activity 往后导航).


从 FragmentManager 获得一个 FragmentTransaction 实例 :?


[java]? view plain copy


  1. FragmentManager?fragmentManager?=getFragmentManager();??

  2. FragmentTransaction?fragmentTransaction?=fragmentManager.beginTransaction();??


每一个事务都是同时要执行的一套变化.可以在一个给定的事务中设置你想执行的所有变化,使用诸如 add()、remove()和 replace().然后, 要给 activity 应用事务, 必须调用 commit().


在调用 commit()之前, 你可能想调用 addToBackStack(),将事务添加到一个 fragment 事务的 backstack. 这个 back stack 由 activity 管理, 并允许用户通过按下 BACK 按键返回到前一个 fragment 状态.


举个例子, 这里是如何将一个 fragment 替换为另一个, 并在后台堆栈中保留之前的状态:


[java]? view plain copy


  1. //?Create?new?fragment?and?transaction??

  2. Fragment?newFragment?=?newExampleFragment();??

  3. FragmentTransaction?transaction?=getFragmentManager().beginTransaction();??

  4. //?Replace?whatever?is?in?thefragment_container?view?with?this?fragment,??

  5. //?and?add?the?transaction?to?the?backstack??

  6. transaction.replace(R.id.fragment_container,newFragment);??

  7. transaction.addToBackStack(null);??

  8. //?Commit?the?transaction??

  9. transaction.commit();??


在这个例子中,newFragment 替换了当前 layout 容器中的由 R.id.fragment_container 标识的 fragment.通过调用 addToBackStack(), replace 事务被保存到 back stack,因此用户可以回退事务,并通过按下 BACK 按键带回前一个 fragment.


如果添加多个变化到事务(例如 add()或 remove())并调用 addToBackStack(),然后在你调用 commit()之前的所有应用的变化会被作为一个单个事务添加到后台堆栈, BACK 按键会将它们一起回退.


添加变化到 FragmentTransaction 的顺序不重要, 除以下例外:?


  • 必须最后调用 commit().

  • 如果添加多个 fragment 到同一个容器, 那么添加的顺序决定了它们在 view hierarchy 中显示的顺序.


当执行一个移除 fragment 的事务时, 如果没有调用 addToBackStack(), 那么当事务提交后,那个 fragment 会被销毁,并且用户不能导航回到它. 有鉴于此, 当移除一个 fragment 时,如果调用了 addToBackStack(), 那么 fragment 会被停止, 如果用户导航回来,它将会被恢复.


提示:?对于每一个 fragment 事务, 你可以应用一个事务动画,通过在提交事务之前调用 setTransition()实现.


调用 commit() 并不立即执行事务.恰恰相反, 它将事务安排排期, 一旦准备好,就在 activity 的 UI 线程上运行(主线程).如果有必要, 无论如何, 你可以从你的 UI 线程调用 executePendingTransactions()来立即执行由 commit()提交的事务. 但这么做通常不必要,除非事务是其他线程中的任务的一个从属.


警告:你只能在 activity 保存它的状态(当用户离开 activity)之前使用 commit()提交事务.


与 Activity 通信




尽管 Fragment 被实现为一个独立于 Activity 的对象,并且可以在多个 activity 中使用,但一个给定的 fragment 实例是直接绑定到包含它的 activity 的. 特别的,fragment 可以使用 getActivity() 访问 Activity 实例, 并且容易地执行比如在 activity layout 中查找一个 view 的任务.


[java]? view plain copy


  1. View?listView?=getActivity().findViewById(R.id.list);<span?style="font-family:System;">?</span>??


同样地,activity 可以通过从 FragmentManager 获得一个到 Fragment 的引用来调用 fragment 中的方法, 使用 findFragmentById() 或 findFragmentByTag().


[java]? view plain copy


  1. ExampleFragment?fragment?=(ExampleFragment)?getFragmentManager().findFragmentById(R.id.example_fragment);??

为 Activity 创建事件回调方法

在一些情况下, 你可能需要一个 fragment 与 activity 分享事件. 一个好的方法是在 fragment 中定义一个回调的 interface, 并要求宿主 activity 实现它.当 activity 通过 interface 接收到一个回调, 必要时它可以和在 layout 中的其他 fragment 分享信息.


例如, 如果一个新的应用在 activity 中有 2 个 fragment – 一个用来显示文章列表(framgent A), 另一个显示文章内容(fragment B) – 然后 framgent A 必须告诉 activity 何时一个 list item 被选中,然后它可以告诉 fragmentB 去显示文章.


在这个例子中, OnArticleSelectedListener 接口在 fragment A 中声明:


[java]? view plain copy


  1. public?static?class?FragmentA?extends?ListFragment?{??

  2. ????...??

  3. ????//?Container?Activity?must?implement?this?interface??

  4. ????public?interface?OnArticleSelectedListener?{??

  5. ????????public?void?onArticleSelected(Uri?articleUri);??

  6. ????}??

  7. ????...??

  8. }??


然后 fragment 的宿主 activity 实现 OnArticleSelectedListener 接口, 并覆写 onArticleSelected() 来通知 fragment B,从 fragment A 到来的事件.为了确保宿主 activity 实现这个接口, fragment A 的 onAttach() 回调方法(当添加 fragment 到 activity 时由系统调用) 通过将作为参数传入 onAttach()的 Activity 做类型转换来实例化一个 OnArticleSelectedListener 实例.


[java]? view plain copy


  1. public?static?class?FragmentA?extends?ListFragment?{??

  2. ????OnArticleSelectedListener?mListener;??

  3. ????...??

  4. ????@Override??

  5. ????public?void?onAttach(Activity?activity)?{??

  6. ????????super.onAttach(activity);??

  7. ????????try?{??

  8. ????????????mListener?=?(OnArticleSelectedListener)?activity;??

  9. ?????????}?catch?(ClassCastException?e)?{??

  10. ????????????throw?new?ClassCastException(activity.toString()?+?"?must?implementOnArticleSelectedListener");??

  11. ????????}??

  12. ????}??

  13. ????...??

  14. }??


如果 activity 没有实现接口, fragment 会抛出 ClassCastException 异常. 正常情形下,mListener 成员会保持一个到 activity 的 OnArticleSelectedListener 实现的引用, 因此 fragment A 可以通过调用在 OnArticleSelectedListener 接口中定义的方法分享事件给 activity.例如, 如果 fragment A 是一个 ListFragment 的子类, 每次用户点击一个列表项, 系统调用在 fragment 中的 onListItemClick(),然后后者调用 onArticleSelected() 来分配事件给 activity.


[java]? view plain copy


  1. public?static?class?FragmentA?extends?ListFragment?{??

  2. ????OnArticleSelectedListener?mListener;??

  3. ????...??

  4. ????@Override??

  5. ????public?void?onListItemClick(ListView?l,?View?v,?int?position,?long?id)?{??

  6. ????????//?Append?the?clicked?item's?row?ID?with?the?content?provider?Uri??

  7. ?????????Uri?noteUri?=ContentUris.withAppendedId(ArticleColumns.CONTENT_URI,?id);??

  8. ????????//?Send?the?event?and?Uri?to?the?host?activity??

  9. ???????mListener.onArticleSelected(noteUri);??

  10. ????}??

  11. ????...??

  12. }??

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
android之Fragment(官网资料翻译)