本章介紹了如何使用 ViewPager(準確地說,應(yīng)該是使用了 FragmentStatePagerAdapter 的簡單的 ViewPager)。
GitHub 地址:
完成第十一章
1. ViewPager 和 PagerAdapter
ViewPager 在某種程度上類似于 RecyclerView,它們都需要借助于 Adapter 來支持,ViewPager 需要的是 PagerAdapter。
ViewPager 與 PagerAdapter 之間的配合比 RecyclerView 與其 Adapter 之間復(fù)雜得多。但是對于本章而言,因為使用的是 PagerAdapter 的子類 FragmentStatePagerAdapter,它能協(xié)助處理很多細節(jié)問題.
FragmentStatePagerAdapter 化繁為簡,提供了兩個有用的方法:getCount() 和 getItem (int)。
調(diào)用 getCount() 方法顧名思義就是獲取數(shù)據(jù)集的大小。調(diào)用 getItem(int) 方法,返回的是應(yīng)該是和數(shù)據(jù)綁定的Fragment,一般來說會將其和數(shù)據(jù)集的位置相對應(yīng)。
使用步驟:
- 布局文件,使用 ViewPager(因為只有支持包而沒有內(nèi)置,所以不像 fragment 需要選擇)
- 在代碼中聲明 ViewPager 變量并引用
- 本書中使用的是匿名 FragmentStatePagerAdapter 類,在其中直接重寫了兩個關(guān)鍵方法,然后就可以使用了。
// 引用 ViewPager
mViewPager = (ViewPager) findViewById(R.id.activity_crime_pager_view_pager);
// 獲取數(shù)據(jù)集
mCrimes = CrimeLab.get(this).getCrimes();
// 獲取 FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
// 使用匿名內(nèi)部類來引用 FragmentStatePagerAdapter,構(gòu)造方法的參數(shù)是 FragmentManager
mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
@Override
public Fragment getItem(int position) {
Crime crime = mCrimes.get(position);
return CrimeFragment.newInstance(crime.getId());
}
@Override
public int getCount() {
return mCrimes.size();
}
});
// 設(shè)置完 Adapter 以后,就要選擇當前的數(shù)據(jù)啦~這就是上一個 Activity 傳遞進來的數(shù)據(jù),我就不予贅述了。
for (int i = 0; i < mCrimes.size(); i++) {
if (mCrimes.get(i).getId().equals(crimeId)) {
mViewPager.setCurrentItem(i);
break;
}
}
2. FragmentStatePagerAdapter 與 FragmentPagerAdapter
FragmentPagerAdapter 是另外一種可用的 PagerAdapter , 其用法與 FragmentStatePagerAdapter 基本一致。唯一的區(qū)別在于:卸載不再需要的 fragment 時,各自采用的處理方法有所不同。
FragmentStatePagerAdapter會銷毀不需要的 fragment。事務(wù)提交后,activity 的 FragmentManager 中的 fragment 會被徹底移除。FragmentStatePagerAdapter 類名中的“state”表明:在銷毀 fragment 時,可在 onSaveInstanceState(Bundle) 方法中保存 fragment 的 Bundle 信息。用戶切換回來時,保存的實例狀態(tài)可用來恢復(fù)生成新的fragment。
相比之下,FragmentPagerAdapter 有不同的做法。對于不再需要的 fragment,FragmentPagerAdapter 會選擇調(diào)用事務(wù)的 detach(Fragment) 方法來處理它,而非 remove(Fragment) 方 法。也就是說,FragmentPagerAdapter 只是銷毀了 fragment 的視圖,fragment 實例還保留在 FragmentManager 中。因此,FragmentPagerAdapter 創(chuàng)建的 fragment 永遠不會被銷毀。
選擇哪種adapter取決于應(yīng)用的要求。通常來說,使用 FragmentStatePagerAdapter 更節(jié)省內(nèi)存。CriminalIntent 應(yīng)用需顯示大量crime記錄,每份記錄最終還會包含圖片。在內(nèi)存中保存所有信息顯然不合適,因此我們選擇使用 FragmentStatePagerAdapter 。
另一方面,如果用戶界面只需要少量固定的fragment,則 FragmentPagerAdapter 是個安全、 合適的選擇。
最常見的例子為分頁顯示用戶界面。例如,某些應(yīng)用的明細視圖所含內(nèi)容較多,通 常需分兩頁顯示。這時就可以將這些明細信息分拆開來,以多頁面的形式展現(xiàn)。顯然,為用戶界面添加支持滑動切換的 ViewPager,能增強應(yīng)用的觸摸體驗。此外,將 fragment 保存在內(nèi)存中,更易于管理控制層的代碼。對于這種類型的用戶界面,每個 activity 通常只有兩三個 fragment,基本不用擔心有內(nèi)存不足的風險。
3. 深入學習:ViewPager 的工作原理
什么時候需要自己實現(xiàn)PagerAdapter接口呢?需要ViewPager托管非 fragment 視圖時,就需要實現(xiàn)原生 PagerAdapter 接口。
PagerAdapter 要比 RecyclerView 的 Adapter復(fù)雜得多,因為它要處理更多的視圖管理工作。
PagerAdapter 不使用可返回視圖的onBindViewHolder(...)方法,而是使用下列方法:
public Object instantiateItem(ViewGroup container, int position)
public void destroyItem(ViewGroup container, int position, Object object)
public abstract boolean isViewFromObject(View view, Object object)
PagerAdapter.instantiateItem(ViewGroup, int)方法告訴 PagerAdapter 創(chuàng)建指定位置的列表項視圖,然后將其添加給 ViewGroup 視圖容器,而destroyItem(ViewGroup, int, Object)方法則告訴 PagerAdapter 銷毀已建視圖。(注意,instantiateItem(ViewGroup, int)方法并不要求立即創(chuàng)建視圖。因此,PagerAdapter 可自行決定何時創(chuàng)建視圖。)視圖創(chuàng)建完成后,ViewPager 會在某個時間點注意到它。為確定該視圖所屬的對象,ViewPager 會調(diào)用
isViewFromObject(View, Object)方法。這 里 , Object 參數(shù)是instantiateItem(ViewGroup,int)方法返回的對象。因此,假設(shè) ViewPager 調(diào)用instantiateItem(ViewGroup, 5)方法返回一個 A 對象,那么只要傳入的 View 參數(shù)是第5個對象的視圖,isViewFromObject(View, A)方法就應(yīng)返回true值,否則返回false值。
對 ViewPager 來說,這是一個復(fù)雜的過程,但對于PagerAdapter來說,這算不上什么。因為PagerAdapter只要能夠創(chuàng)建、銷毀視圖以及識別視圖來自哪個對象即可。這樣的要求顯然很寬松,因而PagerAdapter 能夠比較自由地通過 instantiateItem(ViewGroup, int) 方法創(chuàng)建并添加新的fragment ,然后返回可以跟蹤管理的 Object(fragment) 。
以下為isViewFromObject (View, Object)方法的具體實現(xiàn):
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
可以看到,每次需要使用ViewPager時,都要覆蓋實現(xiàn)PagerAdapter的這些方法,這真是一種磨難。所幸我們有 FragmentPagerAdapter 和 FragmentStatePagerAdapter 這么便利的類。
GitHub Page: kniost.github.io
簡書:http://www.itdecent.cn/u/723da691aa42