文档章节

Fragment懒加载其实很简单

天王盖地虎626
 天王盖地虎626
发布于 06/24 23:03
字数 1886
阅读 10
收藏 0

前言

记得去年面试的时候, 面了一家小公司, 那个面试官问我, fragment的懒加载做过吗?我说没做过(确实没做过).后来面试快结束了, 又问我, 懒加载没做过是吗?后来可想而知也没收到offer, (ಥ_ಥ)
一直对这个问题耿耿于怀, 不就是fragment的懒加载吗, 有那么难?于是这几天看了一下网上的一些方案, 最终觉得还是自己亲手敲一遍好.

原理

懒加载的原理其实挺简单的, 最主要的就是利用fragment中的setUserVisibleHint(boolean isVisibleToUser)方法中传进来的那个isVisibleToUser这个参数, 这个参数的字面意思是表示当前fragment是否对用户可见.注意fragment还有一个getUserVisibleHint()的方法, 这个方法在我看来其实没什么用, 因为我试过打印这个方法的返回值, 返回为true并不能保证用户切换到了当前fragment.

重写setUserVisibleHint()

首先定义一个基类, BaseLazyFragment
重写setUserVisibleHint()这个方法

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.d(TAG, "setUserVisibleHint, isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        // 如果还没有加载过数据 && 用户切换到了这个fragment
        // 那就开始加载数据
        if (!mHaveLoadData && isVisibleToUser) {
            loadDataStart();
            mHaveLoadData = true;
        }
    }

用一个布尔变量mHaveLoadData来表示该fragment是否加载过数据. 如果没有加载过数据, 并且isVisibleToUsertrue(表示用户切换到了这个fragment), 那就开始加载数据, 然后标记该fragment已经加载过数据

loadDataStart()

加载数据这个方法是基类里的一个抽象方法, 需要子类来重写, 因为具体的加载过程是需要子类自己来实现的.
写到这里我突然想到了那个抽象类和接口有什么区别的面试题.
在这里的情景的话, 抽象类就是帮子类统一处理了一些逻辑, 比如判断什么时候需要进行加载数据, 这是由父类帮我们做好的, 子类就不需要再写重复的代码了, 而具体的请求过程是由子类自己去实现的.抽象类在这里的作用就是将重复的逻辑统一处理.
而接口的作用, 就我自己来说用的最多的就是使用一个接口类型的变量来引用一个对象, 这样就不用去关心这个对象具体是什么, 而我们只要知道这个对象中一定有接口中的方法, 到时候我们就能调用这个对象的方法, 虽然我们并不知道方法中的具体逻辑.
扯远了.
现在我们写一个子类继承这个基类fragment

    @Override
    public void loadDataStart() {
        Log.d(TAG, "loadDataStart");
        // 模拟请求数据
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mData = "这是加载下来的数据";
                // 一旦获取到数据, 就应该立刻标记数据加载完成
                mLoadDataFinished = true;
                if (mViewInflateFinished) {
                    mTextView.setVisibility(View.VISIBLE);
                    mTextView.setText(mData);
                    mTextView.setText("这是改变后的数据");
                    mPb.setVisibility(View.GONE);
                }
            }
        }, 3000);
    }

在具体的fragment中模拟请求数据, 在请求完成后将mLoadDataFinished这个变量置为true, 这个字段是继承自基类fragment的. 然后再判断是否布局加载和找控件已经完成, 防止将数据设置到控件上的时候出现控件空指针的错误.
那么我们是在那里将mViewInflateFinished置为true的呢?在去看基类

onCreateView()

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        if (mRootView != null) {
            return mRootView;
        }
        mRootView = initRootView(inflater, container, savedInstanceState);
        findViewById(mRootView);
        mViewInflateFinished = true;
        return mRootView;
    }

我们回到基类看onCeateView()方法, 在这里将布局用一个mRootView的全局变量储存起来是因为当viewpager中的fragment比较多的时候, 切换到别的fragment会导致回调onDestroyView()方法, 再切回来的时候导致onCreateView()onViewCreate()方法又被调用, 为了防止fragment重新从layout文件中加载布局导致之前设置到控件上的变量和状态丢失, 在布局初次加载完成之后用mRootView这个变量储存起来, 当这个变量不为null时就直接复用这个布局就好了.
initRootView()是初次从layout文件加载布局的方法, 是一个抽象方法, 由子类具体去实现, 返回的View表示fragment的布局.
在初次加载布局完成之后就是找控件的findViewById()方法了, 这也是个抽象方法, 需要子类自己去实现.
findViewById()完成之后就将mViewInflateFinished置为true, 表示加载布局和找控件完成.

findViewById()

我们再去看子类具体实现的findViewById()方法.

    @Override
    protected void findViewById(View view) {
        mTextView = view.findViewById(R.id.section_label);
        mPb = view.findViewById(R.id.pb);
        if (mLoadDataFinished) { // 一般情况下这时候数据请求都还没完成, 所以不会进这个if
            mTextView.setVisibility(View.VISIBLE);
            mTextView.setText(mData);
            mPb.setVisibility(View.GONE);
        }
    }

在这个方法里, 如果找控件完成之后, 我们立刻将数据设置到控件上.但是在这之前我们还是需要判断一下是否数据已经加载完成, 如果数据已经加载完成, 那就将数据设置到控件上.这个mLoadDataFinished标志位在上面已经提过.

initRootView

这个方法其实没什么好说的

    @Override
    protected View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.d(TAG, "initRootView");
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

完整代码

再来贴一下基类和子类的完整代码
BaseLazyFragment

public abstract class BaseLazyFragment extends Fragment {

    public final String TAG = getClass().getSimpleName();

    public boolean mHaveLoadData; // 表示是否已经请求过数据

    public boolean mLoadDataFinished; // 表示数据是否已经请求完毕
    private View mRootView;

    // 表示开始加载数据, 但不表示数据加载已经完成
    public abstract void loadDataStart();

    // 表示找控件完成, 给控件们设置数据不会报空指针了
    public boolean mViewInflateFinished;

    @Override
    public void onAttach(Context context) {
        Log.d(TAG, "onAttach");
        super.onAttach(context);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onActivityCreated");
        super.onActivityCreated(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        if (mRootView != null) {
            return mRootView;
        }
        mRootView = initRootView(inflater, container, savedInstanceState);
        findViewById(mRootView);
        mViewInflateFinished = true;
        return mRootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.d(TAG, "onViewCreated");
    }

    protected abstract void findViewById(View view);

    protected abstract View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState);

    @Override
    public void onDestroyView() {
        Log.d(TAG, "onDestroyView");
        super.onDestroyView();
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public void onDetach() {
        Log.d(TAG, "onDetach");
        super.onDetach();
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.d(TAG, "setUserVisibleHint, isVisibleToUser = " + isVisibleToUser);
        super.setUserVisibleHint(isVisibleToUser);
        // 如果还没有加载过数据 && 用户切换到了这个fragment
        // 那就开始加载数据
        if (!mHaveLoadData && isVisibleToUser) {
            loadDataStart();
            mHaveLoadData = true;
        }
    }

}

子类PlaceholderFragment0

public class PlaceholderFragment0 extends BaseLazyFragment {

    private TextView mTextView;
    private ProgressBar mPb;
    private Handler mHandler = new Handler();
    private String mData;

    public PlaceholderFragment0() {
    }

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static PlaceholderFragment0 newInstance() {
        PlaceholderFragment0 fragment = new PlaceholderFragment0();
        return fragment;
    }

    @Override
    public void loadDataStart() {
        Log.d(TAG, "loadDataStart");
        // 模拟请求数据
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mData = "这是加载下来的数据";
                // 一旦获取到数据, 就应该立刻标记数据加载完成
                mLoadDataFinished = true;
                if (mViewInflateFinished) { // mViewInflateFinished一般都是true
                    mTextView.setVisibility(View.VISIBLE);
                    mTextView.setText(mData);
                    mTextView.setText("这是改变后的数据");
                    mPb.setVisibility(View.GONE);
                }
            }
        }, 3000);
    }

    @Override
    protected View initRootView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        Log.d(TAG, "initRootView");
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    protected void findViewById(View view) {
        mTextView = view.findViewById(R.id.section_label);
        mPb = view.findViewById(R.id.pb);
        if (mLoadDataFinished) { // 一般情况下这时候数据请求都还没完成, 所以不会进这个if
            mTextView.setVisibility(View.VISIBLE);
            mTextView.setText(mData);
            mPb.setVisibility(View.GONE);
        }
    }

}

完结

本文转载自:https://www.jianshu.com/p/5c5c778d862e

天王盖地虎626

天王盖地虎626

粉丝 28
博文 487
码字总数 20672
作品 0
南京
私信 提问
《Android-Fragment懒加载》

Fragment懒加载看见这个标题的时候很多人都会问为什么Fragment要懒加载,或者在哪种场景需要懒加载? 带着这两个疑问我们开始往下看。 1、为什么Fragment要懒加载 这个原因有点简单,就是想在...

吕檀溪
2016/08/22
98
0
浅析:Android--Fragment的懒加载

我们都知道,fragment放在viewPager里面,viewpager会帮我们预先加载一个,但是当我们要看fragment里面的内容时,我们也许只会去看第一个,不会去看第二个,如果这时候不去实现fragment的懒加...

红豆与绿豆
2015/10/30
0
0
Android 页面多状态布局管理

一、现状 页面多状态布局是开发中常见的需求,即页面在不同状态需要显示不同的布局,实现的方式也比较多,最简单粗暴的方式就是在 XML 中先将不同状态对应的布局隐藏起来,根据需要改变其可见...

SheHuan
2018/10/15
0
0
【Android】Fragment懒加载

前言 1.Fragment+ViewPager一块使用的时候,其实懒加载是ViewPager中就有的,看过ViewPager源码的人都知道,ViewPager中有设置默认的懒加载的页数,默认的是1,但是这个1可不是我们通常说的一...

定陶黄公子
2016/11/30
228
0
Fragment + viewpager 实现 懒加载 封装

Fragment + viewpager 实现 懒加载 封装,fragment实现懒加载,有几个前提,首先是fragment已经执行了oncreateview方法(isPrepared),其次是fragment可见(isVisiable),最后是fragment需要的...

移动开发
2017/12/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

抽象同步队列AQS——AbstractQueuedSynchronizer锁详解

AQS——锁的底层支持 谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资...

须臾之余
今天
3
0
springboot配置百度UEditor 富文本详解

富文本简介 UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码... 准备工作 ueditor需要单独文...

wotrd
昨天
4
0
mysql 5.7之my.cnf配置大全

[client]port = 3306socket = /tmp/mysql.sock[mysqld]###############################基础设置######################################Mysql服务的唯一编号 每个mysql服务...

Online_Reus
昨天
3
0
MAVEN打包时引入外部链接的包

1.项目引入了ORACLE的jar包,MAVEN配置如下 2.打jar包的时候需要指定下main入口函数mainClass <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> ......

Cobbage
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部