Navigation之详细聊聊Fragment的实现原理

1. 前言

Fragment真是一个非常老的家伙,它的第一条提交记录是在2010年,而最近的一条记录则是在2021年6月11号,足足11岁了,但是它却老当益壮,在Jetpack中大放异彩,Navigation组件就是基于Fragment的一个跳转组件,Google的单Activity项目结构就是一个Activity和多个Fragment项目结构。多年以来,一提到Fragment,大家脑海中的第一印象可能还停留在平板应用开发中了,它曾经在手机项目中高频使用Fragment的机会还真没那么多。一方面是因为手机项目一般都是多Activity结构实现的,不会涉及到那么多的Fragment使用,另一方面,Fragment也比Activity复杂,相比之下使用有点麻烦,甚至是痛苦,不好定位问题,所以在技术选型时,能避则避。它太难用了,以至于Square团队的Piwai大神(LeakCanary的作者)在2014年发表了一篇倡导反对使用Fragment的文章。不管怎么样,这么多年过去了,Android团队也一直没有放弃对Fragment的支持,甚至在Jetpack中,Fragment开始扮演越来越重要的角色了,最近关于Navigation的倡导使用越来越多了,加上我参与了一个平板项目,所以对Fragment主流程源码进行了一次全面的研究。收获颇多,相信将来对Navigation研究定是大有裨益。

2. 创建Fragment

根据官方文档,写一个简单的Demo。简单了解Fragment的使用。

2.1 新建一个Fragment类

class ExampleFragment extends Fragment {
    public ExampleFragment() {
        super(R.layout.example_fragment);
    }
}
复制代码

2.2 把Fragment添加到Activity上

把Fragment添加到Activity上可以通过xml文件和代码编程两种方式实现。本文只介绍第二种实现方式。

<!-- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
复制代码
public class ExampleActivity extends AppCompatActivity {
    public ExampleActivity() {
        super(R.layout.example_activity);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                .setReorderingAllowed(true)
                .add(R.id.fragment_container_view, ExampleFragment.class, null)
                .commit();
        }
    }
}
复制代码

我们可以看到,主要是通过FragmentManager来将Fragment添加到fragment_container_view对应的布局中,Demo是蛮简单的,模板代码,网上拷贝一下就能用。但是FragmentManager、Transaction、commit()分别是什么意思,还有待深入探究一番。

3. 深入理解Fragment原理

3.1 Fragment本质是什么

3.1.1 Fragment本质是View

从前面的DEMO我们看到,Fragment会add到FragmentContainerView上面,能被添加到ViewGroup的组件,一定也是View或者ViewGroup。有源码为证:

//androidx.fragment.app.Fragment.java

ViewGroup mContainer;

// The View generated for this fragment.
View mView;
复制代码

那么既然如此,何必大费周章呢,直接用View来替代Fragment不就好了,addView()、removeView()使用起来更简单直观。

当然了Fragment本质是View但是又不仅限于此, 在真实的项目中,界面往往很复杂,业务逻辑也很复杂,往往需要处理各种UI状态变化,比如:增加一组View,替换一组View,删除一组View,还需要和Activity协作。总之,如果让开发者自己去实现这一套逻辑,恐怕比使用Fragment要麻烦的多吧。尽管Fragment看起来蛮复杂的,但是还是远比我们自己去实现相同功能简单的多。

3.1.2 Fragment本质不仅限于View

Fragment作为一个View可以被添加到Activity的布局中去。但是他更强大,还有以下几个特性:

  1. 处理生命周期,比Activity生命周期还要复杂
  2. 通过FragmentTransaction操作多个Fragment
  3. 通过FragmentManager维护一个回退栈,回退到上一个FragmentTransaction操作的界面
//androidx.fragment.app.Fragment.java

static final int INITIALIZING = -1;          // Not yet attached.
static final int ATTACHED = 0;               // Attached to the host.
static final int CREATED = 1;                // Created.
static final int VIEW_CREATED = 2;           // View Created.
static final int AWAITING_EXIT_EFFECTS = 3;  // Downward state, awaiting exit effects
static final int ACTIVITY_CREATED = 4;       // Fully created, not started.
static final int STARTED = 5;                // Created and started, not resumed.
static final int AWAITING_ENTER_EFFECTS = 6; // Upward state, awaiting enter effects
static final int RESUMED = 7;                // Created started and resumed.
复制代码

从代码我们可以看出,有8种状态。分为两类,View相关的以及和Activity状态相关的。后文会详细介绍,此处点到为止。

3.2 FragmentTransaction

3.2.1 FragmentTransaction概述

软件工程中有一门叫数据库的课程,数据库中有一个叫"事务"的概念,它表示多个操作是原子性的,要么一起成功,要么一起失败,典型的例子就是转账,张三给李四转账100元这么一个事务,存在两个操作,从张三的账户中扣除100元,给李四的账户增加100元。因为有了事务,不会存在只有某一个操作成功,而另一个操作失败的情况,否则后果不堪设想。

在Android中SharedPreference也有commit方法。

SharedPreferences sharedPreferences= getSharedPreferences("data",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("name", “Tom”);
editor.putInt("age", 28);
editor.putBoolean("marrid",false);           
editor.commit();
复制代码

上述功能,一次提交做了三件事情,putString,putInt,putBoolean。

而FragmentTransaction和SharedPreference类似。它可以同时操作多个Fragment。

假设有布局文件如下:

<LinearLayout android:orientation="vertical">
    
    <ViewGroup1>
        <Fragment1 />
    </ViewGroup1>
    
    <ViewGroup2>
        <Fragment2 />
    </ViewGroup2>
    
    <ViewGroup3>
        <Fragment3 />
    </ViewGroup3>
    
</LinearLayout>
复制代码

假设我们想在ViewGroup1上增加Fragment1,ViewGroup2上用Fragment2替换掉,把ViewGroup3上的Fragment移除掉。

伪代码如下:

get
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值