Android Fragment 详解

设计原理

Android 在 Android 3.0(API 级别 11)中引入了片段,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。由于平板电脑的屏幕比手机屏幕大得多,因此可用于组合和交换 UI 组件的空间更大。利用片段实现此类设计时,您无需管理对视图层次结构的复杂更改。 通过将 Activity 布局分成片段,您可以在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。

例如,新闻应用可以使用一个片段在左侧显示文章列表,使用另一个片段在右侧显示文章 — 两个片段并排显示在一个 Activity 中,每个片段都具有自己的一套生命周期回调方法,并各自处理自己的用户输入事件。 因此,用户不需要使用一个 Activity 来选择文章,然后使用另一个 Activity 来阅读文章,而是可以在同一个 Activity 内选择文章并进行阅读,(摘抄自官方文档)
在这里插入图片描述
Fragment特点

  • Fragment表示Activity中的行为或用户界面部分
  • 可以将多个Fragment组合在一个Activity中
  • 可以在多个Activity中复用同一个Fragment
  • Fragment有自己的生命周期能够接受自己的输入事件
  • Fragment必须镶嵌在Activity中,其生命周期受宿主Activity生命周期影响
  • 我们在运行时可以动态添加和删除Fragment

Fragment 优点

  • 模块化:我们不必把所有的代码放入Activity中,而把代码写入Fragment中
  • 可重用性:多个Activity可以重用一个Fragment
  • 可适配性:可以根据硬件屏幕尺寸,方向,能够方便的实现不同的布局

Fragment的核心类

  • Fragment :Fragment的基类,任何创建Fragment的类都需要集成该类
  • FragmentManager:管理和维护Fragment。他是抽象类,具体的实现类是FragmentManagerImpl。
  • FragmentTransaction:对Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是BackStackRecord。
  • Nested Fragment(Fragment内部嵌套Fragment的能力)是Android 4.2提出的,support-fragment库可以兼容到1.6。通过getChildFragmentManager()能够获得子Fragment的FragmentManager,在子Fragment中可以通过getParentFragment()获得父Fragment。

基本使用

创建Fragment
想要创建Fragment,你必须要继承Fragment,通常至少应该实现一下的生命周期方法

  • onCreate
    系统会在创建Fragment的时候,调用此方法,可以在此方法做暂停或者停止后,恢复数据

  • onCreateView()
    系统会在Fragment首次绘制用户界面的时候调用,此方法返回的view就是就是Fragment的根布局,如果Fragment没有提供UI就可以返回null

  • onPause()
    系统将此方法作为离开Fragment的第一个信号


public class MyFragment extends Fragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onPause() {
        super.onPause();
    }
}

添加布局
onCreateView方法返回的View就是Fragment的根布局

    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment,container,false);
    }
  • 先在xml中定义布局
  • 用LayoutInflater 去加载布局

inflate() 方法带有三个参数:

  • 你想要加载的布局id
  • 将作为扩展布局父项的 ViewGroup。传递 container 对系统向扩展布局的根视图(由其所属的父视图指定)应用布局参数具有重要意义;
  • 指示是否应该在扩展期间将扩展布局附加至 ViewGroup(第二个参数)的布尔值。(在本例中,其值为 false,因为系统已经将扩展布局插入 container — 传递 true 值会在最终布局中创建一个多余的视图组。)

向Activity中添加布局

  • 静态添加:在xml中通过添加,缺点就是一旦添加就不能运行时删除
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <fragment
        android:id="@+id/fragment"
        android:name="com.baidu.bpit.aibaidu.fragment.MyFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>

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

1 为 android:id 属性提供唯一 ID。
2 为 android:tag 属性提供唯一字符串。
3 如果您未给以上两个属性提供值,系统会使用容器视图的 ID。

  • 动态添加:通过编程将Fragment添加到某个现有的ViewGroup,运行时添加,这种比较灵活,建议使用这种方式

1 首先你需要一个容器

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>

2 想要在Activity中添加Fragment需要用到FragmentTransaction 的Api,首先获取一个FragmentTransaction 的实例

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

3 用add方法添加一个Fragment

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFragment();
    }

    private void initFragment() {
        Fragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.fragment, myFragment);
        fragmentTransaction.commit();
    }
}

如何添加没有ui的Fragment

要想添加没有 UI 的片段,请使用 add(Fragment, String) 从 Activity 添加片段(为片段提供一个唯一的字符串“标记”,而不是视图 ID)。 这会添加片段,但由于它并不与 Activity 布局中的视图关联,因此不会收到对 onCreateView() 的调用。因此,您不需要实现该方法。

并非只能为非 UI 片段提供字符串标记 — 您也可以为具有 UI 的片段提供字符串标记 — 但如果片段没有 UI,则字符串标记将是标识它的唯一方式。如果您想稍后从 Activity 中获取片段,则需要使用 findFragmentByTag()。

  • 因为我们使用了support库的Fragment,因此需要使用getSupportFragmentManager()获取FragmentManager。
  • add()是对Fragment众多操作中的一种,还有remove(), replace()等,第一个参数是根容器的id(FrameLayout的id,即”@id/container”),第二个参数是Fragment对象,第三个参数是fragment的tag名,指定tag的好处是后续我们可以通过Fragment1 frag = getSupportFragmentManager().findFragmentByTag(“f1”)从FragmentManager中查找Fragment对象。
  • 在一次事务中,可以做多个操作,比如同时做add().remove().replace()。
  • 调用 commit() 不会立即执行事务,而是在 Activity 的 UI 线程(“主”线程)可以执行该操作时再安排其在线程上运行。不过,如有必要,您也可以从 UI 线程调用 executePendingTransactions() 以立即执行 commit() 提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务

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

管理Fragment
要想管理您的 Activity 中的片段,您需要使用 FragmentManager。要想获取它,请从您的 Activity 调用 getFragmentManager()。

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

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

回退栈
可以把每个Fragment保存到由Activity管理的返回栈中,从而可以实现用户可以返回之前打开的Fragment,类似于回退Activity

实现这个功能需要用到addToBackStack方法,例如:

  private void initFragment() {
        Fragment myFragment = new MyFragment();
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.fragment, myFragment)
                .addToBackStack(null)
                .commit();

    }

在Commit之前调用

与Activity进行通信

  • Fragment可以通过getActivity() 访问 Activity 实例
  • 同样地,您的 Activity 也可以使用 findFragmentById() 或 findFragmentByTag(),通过从 FragmentManager 获取对 Fragment 的引用来调用片段中的方法。

Fragment的生命周期

在这里插入图片描述

  • onAttach():Fragment和Activity相关联时调用。可以通过该方法获取Activity引用,还可以通过getArguments()获取参数。
  • onCreate():Fragment被创建时调用。
  • onCreateView():创建Fragment的布局。
  • onActivityCreated():当Activity完成onCreate()时调用。
  • onStart():当Fragment可见时调用。
  • onResume():当Fragment可见且可交互时调用。
  • onPause():当Fragment不可交互但可见时调用。
  • onStop():当Fragment不可见时调用。
  • onDestroyView():当Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁Fragment时调用。
  • onDetach():当Fragment和Activity解除关联时调用。

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

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

不加回退栈,替换Fragment 会调用onDestroy()和onDetach()。加了回退栈不会调用者俩方法

FragmentTransaction有一些基本方法,下面给出调用这些方法时,Fragment生命周期的变化:

add(): onAttach()->…->onResume()。
remove(): onPause()->…->onDetach()。
replace(): 相当于旧Fragment调用remove(),新Fragment调用add()。
show(): 不调用任何生命周期方法,调用该方法的前提是要显示的Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为true。
hide(): 不调用任何生命周期方法,调用该方法的前提是要显示的Fragment已经被添加到容器,只是纯粹把Fragment UI的setVisibility为false。
detach(): onPause()->onStop()->onDestroyView()。UI从布局中移除,但是仍然被FragmentManager管理。
attach(): onCreateView()->onStart()->onResume()。

参考:https://xiazdong.github.io/2017/06/15/android-fragment/
https://developer.android.com/guide/components/fragments?hl=zh-CN

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值