引言
在使用ViewPager的時(shí)候趴拧,我們常常用Fragment來作為每一個(gè)page的載體。但是ViewPager在默認(rèn)情況下會(huì)直接加載出3個(gè)page的劲藐,所以當(dāng)用戶還在瀏覽第一個(gè)page的時(shí)候八堡,第二個(gè)page已經(jīng)進(jìn)行了加載。如果我們想讓用戶瀏覽到某個(gè)page的時(shí)候聘芜,才加載該page的數(shù)據(jù)該怎么做呢兄渺?
有的朋友知道ViewPager有setOffscreenPageLimit(int limit);
方法,想要通過設(shè)置limt為0來實(shí)現(xiàn)這種效果汰现。但是在該方法內(nèi)部其實(shí)是進(jìn)行過判斷的挂谍,如果limit小于了DEFAULT_OFFSCREEN_PAGES,那么依然是設(shè)置為DEFAULT_OFFSCREEN_PAGES瞎饲,也就是設(shè)置為1.
有的朋友又會(huì)考慮口叙,像Activity那樣通過生命周期來進(jìn)行數(shù)據(jù)加載的控制,但是觀察過ViewPager中Fragment的生命周期的人都知道嗅战,這一套是行不通的妄田。如果不清楚的,可以先看看這篇文章
那么我們就只有考慮其他的方法來進(jìn)行Fragment的懶加載了
通過暴露方法進(jìn)行回調(diào)
ViewPager有一個(gè)page事件的監(jiān)聽方法驮捍,當(dāng)監(jiān)聽到頁面被顯示(選中)的時(shí)候疟呐,就去調(diào)用fragment中的那個(gè)方法,才進(jìn)行數(shù)據(jù)的加載东且。
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
// TODO
fragment.initData();
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
通過setUserVisibleHint來判斷
Fragment中有這么一個(gè)方法setUserVisibleHint(boolean isVisibleToUser)
启具。當(dāng)isVisibleToUser為true的時(shí)候,表明Fragment進(jìn)行了用戶的視線珊泳,當(dāng)isVisibleToUser為false的時(shí)候鲁冯,表示Fragment為不可見的狀態(tài)了拷沸。所以我們可以通過該方法來進(jìn)行懶加載的操作。
可能有的朋友就會(huì)直接想要在setUserVisibleHint中進(jìn)行判斷薯演,然后直接進(jìn)行數(shù)據(jù)的加載撞芍。這樣到底行不行呢?我們先看一下該方法的調(diào)用時(shí)間吧涣仿。
首先我們先觀察一下剛進(jìn)入ViewPager的時(shí)候勤庐,該方法以及生命周期的調(diào)用順序吧
接著我們再看一下,當(dāng)滑動(dòng)到第二個(gè)page的時(shí)候好港,該方法以及生命周期的調(diào)用順序吧:
通過這兩個(gè)page的log打印愉镰,我們可以發(fā)現(xiàn)setUserVisibleHint
方法調(diào)用的時(shí)間是不一樣的。page0是在onCreateView()
方法之前就調(diào)用了钧汹,而page1是在之后才調(diào)用的丈探。所以想要直接在setUserVisibleHint中進(jìn)行判斷,然后直接進(jìn)行數(shù)據(jù)的加載是行不通的拔莱。我們需要進(jìn)行雙重的判斷碗降。但是又在哪里進(jìn)行判斷再加載數(shù)據(jù)呢?
通過觀察方法調(diào)用順序塘秦,應(yīng)該在兩個(gè)進(jìn)行都進(jìn)行判斷讼渊。
private boolean isVisibleToUser; // 判斷界面用戶是否可見
private boolean isViewInitialized; // 判斷View是否創(chuàng)建
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
checkIfLoadData();
}
@Overridepublic
void onViewCreated(View view, Bundle savedInstanceState) {
isViewInitialized = true;
checkIfLoadData();
}
@Override
public void onDestroyView() {
super.onDestroyView();
isViewInitialized = false;
}
private void checkIfLoadData() {
if (isVisibleToUser && isViewInitialized && !isDataInitialized) {
// TODO load data
setUpData();
}
}
通過上面的設(shè)置,好像是沒有什么問題了尊剔。
但是仔細(xì)思考一下爪幻,當(dāng)適配器為FragmentPagerAdapter的時(shí)候,F(xiàn)ragment在limit外的時(shí)候是會(huì)被回收的须误,但是此時(shí)的回收只是對于View的回收挨稿,數(shù)據(jù)都是保留有的,那么在重新返回到該page的時(shí)候京痢,還需要重新去加載一次數(shù)據(jù)嗎奶甘?這里就需要根據(jù)實(shí)際的業(yè)務(wù)需求進(jìn)行具體的分析了,如果不需要重新加載祭椰,那么我們還可以添加一個(gè)標(biāo)志位臭家。
當(dāng)適配器為FragmentStatePagerAdapter的時(shí)候,F(xiàn)ragment的回收是View和data都被回收掉了方淤,但是它會(huì)回調(diào)onSaveInstanceState(Bundle outState)
方法侣监,我們可以在這個(gè)時(shí)候進(jìn)行data的保存。
總結(jié)
關(guān)于Fragment懶加載的文章也有很多臣淤,我這個(gè)也只能當(dāng)作一次小小的記錄。具體的業(yè)務(wù)也需要做具體的封裝窃爷,我對于該部分代碼也做了一點(diǎn)小小的封裝優(yōu)化邑蒋,有興趣的可以一起探討一下姓蜂。
示例代碼