【Android】Fragment懒加载(只加载一次)

Fragment懒加载

封装的支持以下的功能:

1.支持数据的懒加载并且只加载一次

2.提供 Fragment 可见与不可见时回调,支持你在这里进行一些 ui 操作,如显示/隐藏加载框

3.支持 view 的复用,防止与 ViewPager 使用时出现重复创建 view 的问题

第一点应该是比较需要且常用的一点,之前那篇博客里没有考虑到这点应用场景是我的疏忽。稍微讲解一下,有些时候,我们打开一个 Fragment 页面时,希望它是在可见时才去加载数据,也就是不要在后台就开始加载数据,而且,我们也希望加载数据的操作只是第一次打开该 Fragment 时才进行的操作,以后如果再重新打开该 Fragment 的话,就不要再重复的去加载数据了。

具体点说,Fragment 和 ViewPager 一起用时,由于 ViewPager 的缓存机制,在打开一个 Fragment 时,它旁边的几个 Fragment 其实也已经被创建了,如果我们是在 Fragment 的 onCreat() 或者 onCreateView() 里去跟服务器交互,下载界面数据,那么这时这些已经被创建的 Fragment,就都会出现在后台下载数据的情况了。所以我们通常需要在 setUserVisibleHint() 里去判断当前 Fragment 是否可见,可见时再去下载数据,但是这样还是会出现一个问题,就是每次可见时都会重复去下载数据,我们希望的是只有第一次可见时才需要去下载,那么就还需要再做一些判断。这就是要封装个基类来做这些事了,具体代码见后面。

即使我们在 setUserVisibleHint() 做了很多判断,实现了可见时加载并且只有第一次可见时才加载,可能还是会遇到其他问题。比如说,我下载完数据就直接需要对 ui 进行操作,将数据展示出来,但有时却报了 ui 控件 null 异常,这是因为 setUserVisibleHint() 有可能在 onCreateView() 创建 view 之前调用,而且数据加载时间很短,这就可能出现 null 异常了,那么我们还需要再去做些判断,保证在数据下载完后 ui 控件已经创建完成。

除了懒加载,只加载一次的需求外,可能我们还需要每次 Fragment 的打开或关闭时显示数据加载进度。对吧,我们打开一个 Fragment 时,如果数据还没下载完,那么应该给个下载进度或者加载框提示,如果这个时候打开了新的 Fragment 页面,然后又重新返回时,如果数据还没加载完,那么也还应该继续给提示,对吧。这就需要有个 Fragment 可见与不可见时触发的回调方法,并且该方法还得保证是在 view 创建完后才触发的,这样才能支持对 ui 进行操作。

以上,就是我们封装的 BaseFragment 基类要干的活了。下面上代码。

代码

/** * Created by dasu on 2016/9/27. * * Fragment基类,封装了懒加载的实现 * * 1、Viewpager + Fragment情况下,fragment的生命周期因Viewpager的缓存机制而失去了具体意义 * 该抽象类自定义新的回调方法,当fragment可见状态改变时会触发的回调方法,和 Fragment 第一次可见时会回调的方法 * *@see#onFragmentVisibleChange(boolean) *@see#onFragmentFirstVisible() */publicabstractclassBaseFragmentextendsFragment{privatestaticfinalString TAG = BaseFragment.class.getSimpleName();privatebooleanisFragmentVisible;privatebooleanisReuseView;privatebooleanisFirstVisible;privateView rootView;//setUserVisibleHint()在Fragment创建时会先被调用一次,传入isVisibleToUser = false//如果当前Fragment可见,那么setUserVisibleHint()会再次被调用一次,传入isVisibleToUser = true//如果Fragment从可见->不可见,那么setUserVisibleHint()也会被调用,传入isVisibleToUser = false//总结:setUserVisibleHint()除了Fragment的可见状态发生变化时会被回调外,在new Fragment()时也会被回调//如果我们需要在 Fragment 可见与不可见时干点事,用这个的话就会有多余的回调了,那么就需要重新封装一个@OverridepublicvoidsetUserVisibleHint(booleanisVisibleToUser){super.setUserVisibleHint(isVisibleToUser);//setUserVisibleHint()有可能在fragment的生命周期外被调用if(rootView ==null) {return;        }if(isFirstVisible && isVisibleToUser) {            onFragmentFirstVisible();            isFirstVisible =false;        }if(isVisibleToUser) {            onFragmentVisibleChange(true);            isFragmentVisible =true;return;        }if(isFragmentVisible) {            isFragmentVisible =false;            onFragmentVisibleChange(false);        }    }@OverridepublicvoidonCreate(@Nullable Bundle savedInstanceState){super.onCreate(savedInstanceState);        initVariable();    }@OverridepublicvoidonViewCreated(View view, @Nullable Bundle savedInstanceState){//如果setUserVisibleHint()在rootView创建前调用时,那么//就等到rootView创建完后才回调onFragmentVisibleChange(true)//保证onFragmentVisibleChange()的回调发生在rootView创建完成之后,以便支持ui操作if(rootView ==null) {            rootView = view;if(getUserVisibleHint()) {if(isFirstVisible) {                    onFragmentFirstVisible();                    isFirstVisible =false;                }                onFragmentVisibleChange(true);                isFragmentVisible =true;            }        }super.onViewCreated(isReuseView ? rootView : view, savedInstanceState);    }@OverridepublicvoidonDestroyView(){super.onDestroyView();    }@OverridepublicvoidonDestroy(){super.onDestroy();        initVariable();    }privatevoidinitVariable(){        isFirstVisible =true;        isFragmentVisible =false;        rootView =null;        isReuseView =true;    }/**    * 设置是否使用 view 的复用,默认开启    * view 的复用是指,ViewPager 在销毁和重建 Fragment 时会不断调用 onCreateView() -> onDestroyView()      * 之间的生命函数,这样可能会出现重复创建 view 的情况,导致界面上显示多个相同的 Fragment    * view 的复用其实就是指保存第一次创建的 view,后面再 onCreateView() 时直接返回第一次创建的 view    *    *@paramisReuse    */protectedvoidreuseView(booleanisReuse){        isReuseView = isReuse;    }/**    * 去除setUserVisibleHint()多余的回调场景,保证只有当fragment可见状态发生变化时才回调    * 回调时机在view创建完后,所以支持ui操作,解决在setUserVisibleHint()里进行ui操作有可能报null异常的问题    *    * 可在该回调方法里进行一些ui显示与隐藏,比如加载框的显示和隐藏    *    *@paramisVisible true  不可见 -> 可见    *                  false 可见  -> 不可见    */protectedvoidonFragmentVisibleChange(booleanisVisible){    }/**

    * 在fragment首次可见时回调,可在这里进行加载数据,保证只在第一次打开Fragment时才会加载数据,

    * 这样就可以防止每次进入都重复加载数据

    * 该方法会在 onFragmentVisibleChange() 之前调用,所以第一次打开时,可以用一个全局变量表示数据下载状态,

    * 然后在该方法内将状态设置为下载状态,接着去执行下载的任务

    * 最后在 onFragmentVisibleChange() 里根据数据下载状态来控制下载进度ui控件的显示与隐藏

    */protectedvoidonFragmentFirstVisible(){    }protectedbooleanisFragmentVisible(){returnisFragmentVisible;    }}

使用方法

使用很简单,新建你需要的 Fragment 类继承自该 BaseFragment,然后重写两个回调方法,根据你的需要在回调方法里进行相应的操作比如下载数据等即可。

例如:

publicclassCategoryFragmentextendsBaseFragment{privatestaticfinalStringTAG=CategoryFragment.class.getSimpleName();@Nullable@OverridepublicViewonCreateView(LayoutInflaterinflater,@NullableViewGroupcontainer,@NullableBundlesavedInstanceState) {Viewview = inflater.inflate(R.layout.fragment_category, container,false);        initView(view);returnview;    }@Overrideprotectedvoid onFragmentVisibleChange(boolean isVisible) {if(isVisible) {//更新界面数据,如果数据还在下载中,就显示加载框notifyDataSetChanged();if(mRefreshState ==STATE_REFRESHING) {                mRefreshListener.onRefreshing();            }        }else{//关闭加载框mRefreshListener.onRefreshFinish();        }    }@Overrideprotectedvoid onFragmentFirstVisible() {//去服务器下载数据mRefreshState =STATE_REFRESHING;        mCategoryController.loadBaseData();    }}

注意事项

如果想要让 fragment 的布局复用成功,需要重写 viewpager 的适配器里的 destroyItem() 方法,将 super 去掉,也就是不销毁 view。

如果出现切换回来或不相邻的Tab切换时导致空白界面的问题,解决方法:在 onCreateView中复用布局 + ViewPager 的适配器中复写 destroyItem() 方法去掉 super。


http://www.cnblogs.com/dasusu/p/6745032.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269