什么是 Fragment?
Fragment,直译为“碎片”,“片段”。Fragment 示意 FragmentActivity 中的行为或界面的一部分。能够在一个 Activity 中组合多个片段,从而构建多窗格界面,并在多个 Activity 中重复使用某个片段。能够将片段视为 Activity 的模块化组成部分,它具备本人的生命周期,能接管本人的输出事件,并且能够在 Activity 运行时增加或移除片段(这有点像能够在不同 Activity 中重复使用的“子 Activity”)。
片段必须始终托管在 Activity 中,其生命周期间接受宿主 Activity 生命周期的影响。例如,当 Activity 暂停时,Activity 的所有片段也会暂停;当 Activity 被销毁时,所有片段也会被销毁。
不过,当 Activity 正在运行(处于已复原生命周期状态)时,能够独立操纵每个片段,如增加或移除片段。当执行此类片段事务时,也可将其增加到由 Activity 治理的返回栈 — Activity 中的每个返回栈条目都是一条已产生片段事务的记录。借助返回栈,用户能够通过按返回按钮吊销片段事务(后退)。
Fragment 的长处
- Fragment 加载灵便,替换不便。定制你的 UI,在不同尺寸的屏幕上创立适合的 UI,进步用户体验。
- 可复用,页面布局能够应用多个 Fragment,不同的控件和内容能够散布在不同的 Fragment 上。
- 应用 Fragment,能够少用一些 Activity。一个 Activity 能够管辖多个 Fragment。
Fragment 生命周期
Fragment 类的代码与 Activity 十分类似。它蕴含与 Activity 相似的回调办法,如 onCreate()、onStart()、onPause() 和 onStop()。实际上,如果要将现有 Android 利用转换为应用片段,可能只需将代码从 Activity 的回调办法移入片段相应的回调办法中。
通常,至多应实现以下生命周期办法 :
- onCreate() 零碎会在创立片段时调用此办法。当片段经验暂停或进行状态继而复原后,如果心愿保留此片段的根本组件,则应在实现中将其初始化。
- onCreateView() 零碎会在片段首次绘制其界面时调用此办法。如要为片段绘制界面,从此办法中返回的 View 必须是片段布局的根视图。如果片段未提供界面,能够返回 null。
- onPause() 零碎会将此办法作为用户来到片段的第一个信号(但并不总是意味着此片段会被销毁)进行调用。通常,应在此办法内确认在以后用户会话完结后依然无效的任何更改(因为用户可能不会返回)。
可能还想扩大几个子类,而非 Fragment 基类 :
- DialogFragment 显示浮动对话框。应用此类创立对话框可无效代替应用 Activity 类中的对话框辅助办法,因为您能够将片段对话框纳入由 Activity 治理的片段返回栈,从而使用户可能返回革除的片段。
- ListFragment 显示由适配器(如 SimpleCursorAdapter)治理的一系列我的项目,相似于 ListActivity。该类提供几种治理列表视图的办法,如用于解决点击事件的 onListItemClick() 回调。(请留神,显示列表的首选办法是应用 RecyclerView,而非 ListView。在此状况下,需在列表布局中创立蕴含 RecyclerView 的片段。如需理解具体操作办法,请参阅应用 RecyclerView 创立列表)
- PreferenceFragmentCompat 以列表模式显示 Preference 对象的层次结构。此类用于为利用创立设置屏幕。
创立 Fragment,应用自定义界面
片段通常用作 Activity 界面的一部分,并且会将其本人的布局融入 Activity。
如要为片段提供布局,必须实现 onCreateView()
回调办法,Android 零碎会在片段须要绘制其布局时调用该办法。此办法的实现所返回的 View 必须是片段布局的根视图。
如要从 onCreateView() 返回布局,能够通过 XML 中定义的布局资源来扩大布局。为帮忙您执行此操作,onCreateView() 提供了一个 LayoutInflater 对象。
例如,以下这个 Fragment 子类从 example_fragment.xml
文件加载布局:
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);
}
}
传递至 onCreateView()
的 container 参数是片段布局将插入到的父级 ViewGroup(来自 Activity 的布局)。savedInstanceState 参数是在复原片段时,提供上一片段实例相干数据的 Bundle(解决片段生命周期局部对复原状态做了具体论述)。
inflate() 办法带有三个参数 :
- 想要扩大的布局的资源 ID。
- 将作为扩大布局父项的 ViewGroup。传递 container 对系统向扩大布局的根视图(由其所属的父视图指定)利用布局参数具备重要意义。
- 批示是否应在扩大期间将扩大布局附加至 ViewGroup(第二个参数)的布尔值。(在本例中,此值为 false,因为零碎已将扩大布局插入 container,而传递 true 值会在最终布局中创立一个多余的视图组。)
接下来,需将该片段增加到您的 Activity 中。
向 Activity 增加 Fragment
通常,片段会向宿主 Activity 奉献一部分界面,作为 Activity 整体视图层次结构的一部分嵌入到 Activity 中。能够通过两种形式向 Activity 布局增加片段(以下为代码片段,并非残缺代码)。
动态形式
在 Activity 的布局文件内申明片段。在本例中,您能够将片段当作视图来为其指定布局属性。例如,以下是领有两个片段的 Activity 的布局文件:
<?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>
<fragment>
中的 android:name
属性指定要在布局中进行实例化的 Fragment 类。
创立此 Activity 布局时,零碎会将布局中指定的每个片段实例化,并为每个片段调用 onCreateView()
办法,以检索每个片段的布局。零碎会直接插入片段返回的 View,从而代替 <fragment>
元素。
留神:每个片段都须要惟一标识符,重启 Activity 时,零碎可应用该标识符来复原片段(也能够应用该标识符来捕捉片段,从而执行某些事务,如将其移除)。能够通过两种形式为片段提供 ID:
为 android:id 属性提供惟一 ID。
为 android:tag 属性提供惟一字符串。
Java 代码加载 Fragment
或者,通过编程形式将片段增加到某个现有 ViewGroup。在 Activity 运行期间,您能够随时将片段增加到 Activity 布局中。您只需指定要将片段放入哪个 ViewGroup。
如要在 Activity 中执行片段事务(如增加、移除或替换片段),则必须应用 FragmentTransaction 中的 API。如下所示,能够从 FragmentActivity 获取一个 FragmentTransaction 实例:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
而后,能够应用 add()
办法增加一个片段,指定要增加的片段以及将其插入哪个视图。例如:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
传递到 add()
的第一个参数是 ViewGroup,即应搁置片段的地位,由资源 ID 指定,第二个参数是要增加的片段。一旦通过 FragmentTransaction 做出了更改,就必须调用 commit()
以使更改失效。
治理 Fragment
如要治理 Activity 中的片段,需应用 FragmentManager。如要获取它,请从 Activity 调用 getSupportFragmentManager()
。
可应用 FragmentManager 执行的操作包含 :
- 通过
findFragmentById()
(针对在 Activity 布局中提供界面的片段)或 findFragmentByTag()(针对提供或不提供界面的片段)获取 Activity 中存在的片段。 - 通过
popBackStack()
(模仿用户收回的返回命令)使片段从返回栈中弹出。 - 通过
addOnBackStackChangedListener()
注册侦听返回栈变动的侦听器。
也可应用 FragmentManager 关上一个 FragmentTransaction,通过它来执行某些事务,如增加和移除片段。
执行 Fragment 事务
在 Activity 中应用片段的一大长处是,能够通过片段执行增加、移除、替换以及其余操作,从而响应用户交互。提交给 Activity 的每组更改均称为事务,并且可应用 FragmentTransaction 中的 API 来执行一项事务。也可将每个事务保留到由 Activity 治理的返回栈内,从而让用户可能回退片段更改(相似于回退 Activity)。
如下所示,能够从 FragmentManager 获取一个 FragmentTransaction 实例:
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每个事务都是想要同时执行的一组更改。能够应用 add()、remove() 和 replace() 等办法,为给定事务设置您想要执行的所有更改。而后,如要将事务利用到 Activity,必须调用 commit()。
不过,在调用 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() 从返回栈检索片段。
如果向事务增加多个更改(如又一个 add() 或 remove()),并调用 addToBackStack(),则调用 commit() 前利用的所有更改都将作为繁多事务增加到返回栈,并且返回按钮会将它们一并吊销。
向 FragmentTransaction 增加更改的程序无关紧要,不过:
必须最初调用 commit()。如果要向同一容器增加多个片段,则增加片段的程序将决定它们在视图层次结构中呈现的程序。如果没有在执行删除片段的事务时调用 addToBackStack(),则事务提交时该片段会被销毁,用户将无奈回退到该片段。不过,如果在删除片段时调用 addToBackStack(),则零碎会进行该片段,并随后在用户回退时将其复原。
调用 commit() 不会立刻执行事务,而是在 Activity 的界面线程(“主”线程)可执行该操作时,再安顿该事务在线程上运行。不过,如有必要,也能够从界面线程调用 executePendingTransactions(),以立刻执行 commit() 提交的事务。通常不用这样做,除非其余线程中的作业依赖该事务。
留神:只能在 Activity 保留其状态(当用户来到 Activity)之前应用 commit() 提交事务。如果试图在该工夫点后提交,则会引发异样。这是因为如需复原 Activity,则提交后的状态可能会失落。对于失落提交无关紧要的状况,请应用
commitAllowingStateLoss()
。
生命周期变动
Fragment 被创立的时候
它会经验以下状态
onAttach()
onCreate()
onCreateView()
onActivityCreated()
Fragment 对用户可见的时候
它会经验以下状态
onStart()
onResume()
Fragment 进入“后盾模式”的时候
它会经验以下状态
onPause()
onStop()
Fragment 被销毁了(或者持有它的 activity 被销毁了)
它会经验以下状态
onPause()
onStop()
onDestroyView()
onDestroy()
onDetach()
Fragment 与 Activity 不同的生命周期
Fragment 的大部分状态都和 Activity 很类似,但 fragment 有一些新的状态。
Fragment 不同于 Activity 的生命周期
– onAttached()
—— 当 fragment 被退出到 activity 时调用(在这个办法中能够取得所在的 activity)。
– onCreateView()
—— 当 activity 要失去 fragment 的 layout 时,调用此办法,fragment 在其中创立本人的 layout(界面)。
– onActivityCreated()
—— 当 activity 的 onCreated() 办法返回后调用此办法
– onDestroyView()
—— 当 fragment 中的视图被移除的时候,调用这个办法。
– onDetach()
—— 当 fragment 和 activity 拆散的时候,调用这个办法。
一旦 activity 进入 resumed 状态(也就是 running 状态),你就能够自在地增加和删除 fragment 了。因而,只有当 activity 在 resumed 状态时,fragment 的生命周期能力独立的运行,其它时候是依赖于 activity 的生命周期变动的。
解决 Fragment 生命周期
治理片段生命周期与治理 Activity 生命周期很类似。和 Activity 一样,片段也以三种状态存在:
- 已复原:片段在运行中的 Activity 中可见。
- 已暂停:另一个 Activity 位于前台并具备焦点,但此片段所在的 Activity 依然可见(前台 Activity 局部通明,或未笼罩整个屏幕)。
- 已进行:片段不可见。宿主 Activity 已进行,或片段已从 Activity 中移除,但已增加到返回栈。已进行的片段仍处于活动状态(零碎会保留所有状态和成员信息)。不过,它对用户不再可见,并随 Activity 的终止而终止。与 Activity 一样,您也可应用 onSaveInstanceState(Bundle)、ViewModel 和长久化本地存储的组合,在配置变更和过程终止后保留片段的界面状态。如要理解保留界面状态的更多信息,请参阅保留界面状态。
对于 Activity 生命周期与片段生命周期而言,二者最显著的差别是在其各自返回栈中的存储形式。默认状况下,Activity 进行时会被放入由系统管理的 Activity 返回栈中。不过,只有在移除片段的事务执行期间通过调用 addToBackStack() 显式申请保留实例时,零碎才会将片段放入由宿主 Activity 治理的返回栈。
在其余方面,治理片段生命周期与治理 Activity 生命周期十分类似;对此,可采取雷同的做法。
留神:如果 Fragment 中须要 Context 对象,则能够调用 getContext()。但请留神,只有在该片段附加到 Activity 时才需调用 getContext()。如果尚未附加该片段,或者其在生命周期完结期间已拆散,则 getContext() 返回 null。
Fragment 相干面试题:
1. 如何切换 fragement(不从新实例化)
翻看了 Android 官网 Doc,和一些组件的源代码,发现 replace() 这个办法只是在上一个 Fragment 不再须要时采纳的简便办法.
正确的切换形式是 add(),切换时 hide(),add() 另一个 Fragment;再次切换时,只需 hide() 以后,show() 另一个。这样就能做到多个 Fragment 切换不从新实例化:
2. Fragment 的的长处
- Fragment 能够使你可能将 activity 拆散成多个可重用的组件,每个都有它本人的生命周期和 UI。
- Fragment 能够轻松得创立动静灵便的 UI 设计,能够适应于不同的屏幕尺寸。从手机到平板电脑。
- Fragment 是一个独立的模块, 紧紧地与 activity 绑定在一起。能够运行中动静地移除、退出、替换等。
- Fragment 提供一个新的形式让你在不同的安卓设施上对立你的 UI。
- Fragment 解决 Activity 间的切换不晦涩,轻量切换。
- Fragment 代替 TabActivity 做导航,性能更好。
- Fragment 在 4.2. 版本中新增嵌套 fragment 应用办法,可能生成更好的界面成果。
3. Fragment 如何实现相似 Activity 栈的压栈和出栈成果
Fragment 的事物管理器外部维持了一个双向链表构造,该构造能够记录咱们每次 add 的 Fragment 和 replace 的 Fragment,而后当咱们点击 back 按钮的时候会主动帮咱们实现退栈操作。
4. Fragment 的 replace 和 add 办法的区别
Fragment 自身并没有 replace 和 add 办法,这里的了解应该为应用 FragmentManager 的 replace 和 add 两种办法切换 Fragment 时有什么不同。
咱们常常应用的一个架构就是通过 RadioGroup 切换 Fragment,每个 Fragment 就是一个功能模块。
Fragment 的容器一个 FrameLayout,add 的时候是把所有的 Fragment 一层一层的叠加到了 FrameLayout 上了,而 replace 的话首先将该容器中的其余 Fragment 去除掉而后将以后 Fragment 增加到容器中。
一个 Fragment 容器中只能增加一个 Fragment 品种,如果屡次增加则会报异样,导致程序终止,而 replace 则无所谓,轻易切换。
因为通过 add 的办法增加的 Fragment,每个 Fragment 只能增加一次,因而如果要想达到切换成果须要通过 Fragment 的的 hide 和 show 办法联合者应用。将要显示的 show 进去,将其余 hide 起来。这个过程 Fragment 的生命周期没有变动。通过 replace 切换 Fragment,每次都会执行上一个 Fragment 的 onDestroyView,新 Fragment 的 onCreateView、onStart、onResume 办法。
基于以上不同的特点咱们在应用的应用肯定要联合着生命周期操作咱们的视图和数据。
5. Fragment 与 Activity 之间是如何传值的
-
Activity 向 Fragment 传值:
将要传的值,放到 bundle 对象里;在 Activity 中创立该 Fragment 的对象 fragment,
通过调用 fragment.setArguments() 传递到 fragment 中;
在该 Fragment 中通过调用 getArguments() 失去 bundle 对象,就能失去外面的值。 -
Fragment 向 Activity 传值:
在 Activity 中调用 getFragmentManager() 失去 fragmentManager,,调用 findFragmentByTag(tag) 或者通过 findFragmentById(id)
FragmentManager fragmentManager = getFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(tag);
通过回调的形式,定义一个接口(能够在 Fragment 类中定义),接口中有一个空的办法,在 fragment 中须要的时候调用接口的办法,值能够作为参数放在这个办法中,而后让 Activity 实现这个接口,必然会重写这个办法,这样值就传到了 Activity 中。
6. Fragment 生命周期
onAttach(Contextcontext)
:在 Fragment 和 Activity 关联上的时候调用,且仅调用一次。在该回调中咱们能够将 context 转化为 Activity 保留下来,从而防止前期频繁调用 getAtivity() 获取 Activity 的场面,防止了在某些状况下 getAtivity() 为空的异样(Activity 和 Fragment 拆散的状况下)。同时也能够在该回调中将传入的 Arguments 提取并解析,在这里强烈推荐通过 setArguments 给 Fragment 传参数,因为在利用被零碎回收时 Fragment 不会保留相干属性。onCreate
:在最后创立 Fragment 的时候会调用,和 Activity 的 onCreate 相似。View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState)
:在筹备绘制 Fragment 界面时调用,返回值为 Fragment 要绘制布局的根视图,当然也能够返回 null。留神应用 inflater 构建 View 时肯定要将 attachToRoot 指明 false,因为 Fragment 会主动将视图增加到 container 中,attachToRoot 为 true 会反复增加报错。onCreateView 并不是肯定会被调用,当增加的是没有界面的 Fragment 就不会调用,比方调用 FragmentTransaction 的 add(Fragment fragment, String tag) 办法。onActivityCreated
:在 Activity 的 onCreated 执行完时会调用。onStart()
:Fragment 对用户可见的时候调用,前提是 Activity 曾经 started。onResume()
:Fragment 和用户之前可交互时会调用,前提是 Activity 曾经 resumed。onPause()
:Fragment 和用户之前不可交互时会调用。onStop()
:Fragment 不可见时会调用。onDestroyView()
:在移除 Fragment 相干视图层级时调用。onDestroy()
:最终分明 Fragment 状态时会调用。onDetach()
:Fragment 和 Activity 解除关联时调用。
7. ViewPager 对 Fragment 生命周期的影响
ViewPager+Fragment 是比拟常见的组合了,个别搭配 ViewPager 的 FragmentPagerAdapter 或 FragmentStatePagerAdapter 应用。不过 ViewPager 为了避免滑动呈现卡顿,有一个缓存机制,默认状况下 ViewPager 会创立并缓存以后页面左右两边的页面(如 Fragment)。此时左右两个 Fragment 都会执行从 onAttach->….->onResume 的生命周期,明明 Fragment 没有显示却曾经到 onResume 了,在某些状况下会呈现问题。比方数据的加载机会、判断 Fragment 是否可见等。
Android 零根底入门教程视频参考