Fragment完全解析系列(一)基础概念

来源:互联网 发布:知行学院地址 编辑:程序博客网 时间:2024/05/21 15:27

简介

Fragment俗称碎片,在Android3.0后为了提升页面展示效率被Google开发人员提出。它依附于Activity存在,它拥有自己的生命周期,也可以和用户相互。Fragment存在于Activity中,通过Activity可以实现对Fragment的动态添加、删除、灵活显示UI等操作。


Fragment生命周期

Fragment必须依赖于Activity而存在,所以它的生命周期和Activity必定是密不可分的,参考下图。

这里写图片描述

我们抽出和Activity不同的阶段来解读。

onAttach(Context)
当Fragment与Activity发生关联时调用;

onCreateView(LayoutInflater, ViewGroup, Bundle)
创建Fragment的视图;

onActivityCreated(Bundle)
当Activity的onCreate()方式执行时调用;

onDestroyView()
和onCreateView()对应,销毁Fragment视图时调用;

onDetach()
和onAttach()对应,取消与Activity的关联关系时调用;

PS
当我们重写Fragment的方法时,除了onCreateView()方法外,重写其它任何方法时都必须引用其父类方法。


Fragment静态使用

静态使用Fragment是其用法当中最简单的一种,具体的实现:
1. 新建类继承Fragment,重写onCreateView()方法来实现我们自己的布局;
2. 在xml文件中,利用”fragment”控件及其属性”name”将布局与Fragment绑定,在Activity中直接声明控件使用。

举个很简单的例子:

FragmentOne.class
这里写图片描述

FragmentTwo.class
这里写图片描述

MyActivity.class
这里写图片描述

activity_fragment.xml
这里写图片描述

这里写图片描述


Fragment动态使用

相比静态使用,动态使用更加灵活,我们可以在代码中动态的控制Fragment的显示、隐藏、删除,还实现多个Fragment动态切换等操作。了解动态使用的用法其实我们只需要了解一下几个和Fragment相关的类即可。

Fragment.class,用于定义Fragment;
FragmentManager.class,主要用于Activity操作Fragment;
FragmentTranscation.class,Fragment的事务。

1.获取FragmengManager

FragmentManager fm = getFragmentManager();

2.FragmentTranscation事务操作

//开启一个事务FragmentTransaction transaction = fm.beginTransaction();//add,添加fragment到activitytransaction.add(R.id.fragment, mFragmentOne);//remove,如果该fragment没有被添加到回退栈则实例被销毁transaction.remove(mFragmentOne);//replace,使用一个fragment替代另一个fragment,先add后removetransaction.replace(R.id.fragment, mFragmentTwo);//hide,隐藏当前的fragment视图,不销毁实例transaction.hide(mFragmentOne);//show,将当前的fragment显示出来transaction.show(mFragmentOne);//添加到回退栈,我们单独分析它transaction.addToBackStack();//commit,提交事务,熟悉事务的都应该知道,commit之后我们以上操作才会生效transaction.commit();

以上便是Fragment的动态使用相关的API,稍微练习一下,很简单。最重要的是,我们需要考虑这些api会对fragment造成什么影响,哪个销毁fragment的视图,哪个销毁fragment的实例,哪个仅仅控制fragment的可见与否。


Fragment回退栈

Activity切换时,相信大家都知道是通过栈的形式,不断压栈出栈,在Fragment的时候,如果你不是手动开启回退栈,它是直接销毁再重建,但如果将Fragment任务添加到回退栈,情况就会不一样了,它就有了类似Activity的栈管理方式。
我们将fragment的任务添加到回退栈当中去,当我们点击back键时,不会直接退出activity,而将看到上次操作的fragment。
我们可以这样理解,当activity销毁之前,会检查其管理的fragment回退栈中是否存在fragment,如果存在fragment则每按一次back键则弹出一次fragment直到回退栈里不存在fragment时activity才退出。

这里写图片描述

上面的GIF中,有一个Activity三个Fragment,
1.刚开始显示FragmentA;
2.在FragmentA中填写“111”点击跳到FragmentB;
3.在FragmentB中填写“222”点击跳到FragmentC;
4.按下back,从FragmentC返回到FragmentB,数据“222”存在;
5.按下back,从FragmentB返回到FragmentA,数据“111”不存在了;
下面对照代码来分析。

MyActivity.class
这里写图片描述
直接将FragmentOne添加到fragment布局上去,这里之所以没有调用addToBackStack()方法,是为了防止在此处back时引起的Activity白底。

FragmentOne.class
这里写图片描述
从Fragment的动态使用介绍中我们知道,调用replace()方法就是调用remove()、add(),当我们不将事务加入到回退栈时,此Fragment会被销毁。事务加入到回退栈中时只是销毁FragmentA的视图而不会销毁FragmentA的实例,所以back回来的时候存在FragmentA,但是我们输入的“111”不见了。(销毁视图但不销毁实例)。

FragmentTwo.class
这里写图片描述
我们做了两个操作,一隐藏了该Fragment,二将事务加入到回退栈,既没有销毁Fragment的视图也没有销毁Fragment的实例,所以FragmentThree按back返回时,FragmentTwo显示且其数据“222”依旧存在。

FragmentThree.class
这里写图片描述
此段代码什么也没做,可忽略。

PS
在写这个例子时,我惊讶地发现我从FragmentThree开始返回时,FragmentTwo、FragmentOne中的数据都保留了下来,FragmentOne上依然有数据“111”,后来发现是我们创建布局时,给xml中的EditText定义了id,导致自动调用了onSaveInstanceState()方法恢复了数据,才出现以上描述的状况。


Fragment懒加载

这个场景我们应用的还是很多的。比如今日头条APP,上面的标签“推荐”、“热点”、“视频”等,当我们第一次打开APP时,第一次选中标签(该标签下无数据)时自动加载数据,当下次在选中此标签时(标签已有数据)不再加载数据。

这里写图片描述

懒加载的常用场景是Fragment结合Viewpager使用,它的核心就是当Fragment可见并且未加载数据时加载数据,其它情况不再加载数据。满足两个条件,(可见、未加载数据)。可见与否我们可以通过Fragment提供的setUserVisibleHint(boolean isVisibleToUser);方法中的参数得到,即isVisibleToUser为true可见,false不可见。我们定义一个变量来表示是否加载了数据,isLoadDataFlag默认为false,当加载了数据后置为true。这些操作要保证Fragment的视图已创建的,设置一个可以判断是否创建了视图的标志,isViewCreated初始化为false,在onCreateView()中置为true。

我们通过三个标志来判定是否加载数据。

//Fragment视图是否已创建private boolean isViewCreated = false;//Fragment是否加载了数据private boolean isLoadDataSuccess = false;//Fragment是否对用户可见,此变量有api提供boolean isVisibleToUser;

由此,我们来写一个完整的懒加载LazyFragment.class

public abstract class LazyFragment extends Fragment {    //Fragment视图是否已创建    private boolean isViewCreated = false;    //Fragment是否加载了数据    private boolean isLoadDataSuccess = false;    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        View view = inflater.inflate(getLayoutId(), container, false);        initView(view);        isViewCreated = true;        return view;    }    @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        if ( getUserVisibleHint() ){            isLoadDataSuccess = true;            loadData();        }    }    @Override    public void setUserVisibleHint(boolean isVisibleToUser) {        super.setUserVisibleHint(isVisibleToUser);        if ( isVisibleToUser && isViewCreated && !isLoadDataSuccess ){            isLoadDataSuccess = true;            loadData();        }    }    public abstract int getLayoutId();    public abstract void initView(View view);    public abstract void loadData();}

为什么 loadData() 会在两个地方执行?为什么 onActivityCreated 也要执行呢?因为,ViewPager 默认显示第一页,第一页肯定要先加载数据,而且 setUserVisibleHint 的执行顺序又是在 onCreatView 之前,同时 onCreatView 需要初始化界面和修改 isViewCreated 的值。所以就需要在 onActivityCreated 里执行一次。


0 0
原创粉丝点击