Viewpager與fragment的組合在專案中使用的頻率太高,我們對Viewpager的兩種的Adapter的處理方式略顯不足加強一下認識。
先簡單看看Fragment的兩種Adapter的使用方式。
都是PagerAdapter的子類。
1:FragmentStatePagerAdapter
2:FragmentPagerAdapter複製程式碼
當Viewpager.setAdapter(),Viewpager與PagerAdapter建立雙向的聯絡。
1)Viewpager會擁有pagerAdapter的物件,能getAdapter獲取到當前的Adapter。
2)mAdapter.setViewPagerObserver(new PagerObserver),至此得到adapter中的註冊測,能向Viewpager傳遞資料。這樣就算綁在一起了。
注意:呼叫pagerAdapter.notifDataSerChange()觸發時,ViewPager.dataSetChanged()也會被呼叫。該函式使用Adapter中的getitemposition()返回值來進行判斷。
一,PagerAdapter
這是我們常用兩種Adapter的基類。這是一個abstract的,直接繼承他的話我麼需要重寫方法
1)getItemPosition();
//instantiateItem()的返回值將有此函式的返回值進行判斷,是否進行呼叫。
public static final int POSITION_UNCHANGED = -1;
public static final int POSITION_NONE = -2;複製程式碼
如若不重寫,預設返回POSITION_UNCHANGED,將不會呼叫Viewpager的observer.notifit()
因此,呼叫Adapter.notifyDataSetChanged()將不會出現什麼改變。
//後面這三個方法,直接照著官方文件書寫就行!
2)instantiateItem();
3) getCount();
4) isViewFromObject();
5) destroyItem();
二,FragmentStatePagerAdapter
/**
- FragmentStatePagerAdapter 和前面的 FragmentPagerAdapter 一樣,是繼承子 PagerAdapter。
- 但和 FragmentPagerAdapter 不一樣的是,
- 正如其類名中的 'State' 所表明的含義一樣,
- 該 PagerAdapter 的實現將只保留當前頁面,
- 當頁面離開視線後,就會被消除,釋放其資源;
- 而在頁面需要顯示時,生成新的頁面(就像 ListView 的實現一樣)。
- 這麼實現的好處就是當擁有大量的頁面時,不必在記憶體中佔用大量的記憶體。
*/
重寫方法:
1)getItem()
生成需要的Fragment的物件。Fragment.setArgument()這種只會在新建Fragment時執行一次的傳遞引數放在這裡。
2)instantiateItem()
除非碰到FragmentManger剛好從Savedstate 中恢復了對應Fragment的情況外,都將呼叫getItem()來生成新的物件。
mFragmentManager.beginTransaction().add(container.getId(), fragment);
3)destoryItem()
將Fragment移除。
mFragmentManager.beginTransaction().remove(fragment);
三,FragmentPagerAdapter
/**
- 該類內的每一個生成的 Fragment 都將儲存在記憶體之中,
- 因此適用於那些相對靜態的頁,數量也比較少的那種;
- 如果需要處理有很多頁,並且資料動態性較大、佔用記憶體較多的情況,
- 應該使用FragmentStatePagerAdapter。
*/
重寫方法
1)getItem();
只會在第一次初始化的時候地呼叫,每次都會查詢一下是否有當前Fragment生成過。生成了就不呼叫getItem的方法。
2)instantiateItem();
判斷一下要生成的Fragment是否已經生成過了,如果生成了,呼叫mFragmentManager.beginTransaction().attch(fragment);
如果沒有
mFragmentManager.beginTransaction().add(container.getId(), fragment,makeFragmentName(container.getId(), itemId));
3)destoryItem()
mFragmentManager.beginTransaction().detach()。
這裡並不會remove(),因此Fragment還在Fragmentmanager管理中,Fragment所佔用的資源不會釋放。
上訴兩個Adapter 對Fragment的生命週期的最大的不同 FragmentStatePagerAdapter是銷燬重走所有生命走起 onAttach - onDetach
FragmentPagerAdapter 是從onCreateView - onDestroyView 然而成員不銷燬,因此我們有些會出現一些資料錯亂的時候。(這裡我們可以判斷當前的View是否已經載入過了)
if(rootView==null){
rootView=inflater.inflate(R.layout.tab_fragment, null);
}
//快取的rootView需要判斷是否已經被加過parent, 如果有parent需要從parent刪除,要不然會發生這個rootview已經有parent的錯誤。
ViewGroup parent = (ViewGroup) rootView.getParent();
if (parent != null) {
parent.removeView(rootView);
} 複製程式碼
四,生命週期的看法
這裡重點說明一下:setUserVisibleHint 呼叫 在所有生命週期之前執行。每次Adapter切換都會呼叫相應的Fragment的此方法。
思路也就來了。 實現的思路很多種,仔細看這個生命週期,總結規律,這個懶載入也就現實了。
`public abstract class LazzyFragment extends Fragment {
//判斷控制元件是否載入完畢
private boolean isCreateView = false;
//判斷是否已載入過資料
public boolean isLoadData = false;
/**
* 返回layoutView
* @param inflater
* @param container
* @return 初始化佈局檔案
*/
public abstract View getView(LayoutInflater inflater, ViewGroup container);
/**
* 初始化控制元件
*/
public abstract void initViews(View view);
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = getView(inflater, container);
initViews(view);
isCreateView = true;
return view;
}
//注意,此方法再所有生命週期之前呼叫,不可操作控制元件
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && isCreateView && !isLoadData) {
loadData();
}
}
/**
* 載入資料
*/
public void loadData() {
//如果沒有載入過就載入,否則就不再載入了
if (!isLoadData) {
//載入資料操作
isLoadData = true;
}
}
// 第一次進入ViewPager的時候我們需要直接載入,因為此時setUserVisibleHint 已經呼叫過了。
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getUserVisibleHint())
loadData();
}複製程式碼
}`