ViewPager在開發中的使用頻率非常的高,所以在此做個總結。
一、簡介
1.ViewPager的簡介和作用
ViewPager是android擴展包v4包中的類,這個類可以讓用戶左右切換當前的view1)ViewPager類直接繼承了ViewGroup類,所有它是一個容器類,可以在其中添加其他的view類。2)ViewPager類需要一個PagerAdapter適配器類給它提供數據。3)ViewPager經常和Fragment一起使用,并且提供了專門的FragmentPagerAdapter和FragmentStatePagerAdapter類供Fragment中的ViewPager使用。
2.ViewPager的適配器
簡介中提到了PagerAdapter,和ListView等控件使用一樣,需要ViewPager設置PagerAdapter來完成頁面和數據的綁定,這個PagerAdapter是一個基類適配器,我們經常用它來實現app引導圖,它的子類有FragmentPagerAdapter和FragmentStatePagerAdapter,這兩個子類適配器用于和Fragment一起使用,在安卓應用中它們就像listview一樣出現的頻繁。
二、Adapter的工具類
這里只是做了最簡單的封裝,可以根據需要調整
PagerAdapter工具類
public class QuickPageAdapter<T extends View> extends PagerAdapter {
private List<T> mList;
public QuickPageAdapter(List<T> mList) {
this.mList = mList;
}
@Override
public int getCount() {
return mList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return object == view;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mList.get(position));
return mList.get(position);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mList.get(position));
}
}
使用
List<View> views = new ArrayList<>();
mViewPager.setAdapter(new QuickPageAdapter<View>(views));
FragmentPagerAdapter工具類
public class QuickFragmentPageAdapter<T extends Fragment> extends FragmentPagerAdapter {
private List<T> mList;
private String[] mStrings;
/**
* @param fm
* @param list
* @param titles PageTitles
*/
public QuickFragmentPageAdapter(FragmentManager fm, List<T> list, String[] titles) {
super(fm);
mList = list;
mStrings = titles;
}
@Override
public Fragment getItem(int position) {
return mList.get(position);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mStrings == null ? super.getPageTitle(position) : mStrings[position];
}
}
FragmentStatePagerAdapter封裝類似FragmentPagerAdapter就不寫了,基本使用講完了。
三、源碼淺析
FragmentPagerAdapter和FragmentStatePagerAdapter的區別,你造嗎?
先上源碼
<ul>
<li> FragmentStatePagerAdapter</li>
</ul>
@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);//fragment被釋放后這里得到的null值
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);//fragment被釋放后或者是初次進入頁面拿到新的Fragment實例
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);//新的Fragment實例 是add上去的
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
+ " v=" + ((Fragment)object).getView());
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, fragment.isAdded()
? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);//真正釋放了fragment實例
mCurTransaction.remove(fragment);
}
<ul>
<li> FragmentPagerAdapter</li>
</ul>
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);//因為fragment實例沒有被真正釋放,所以可以直接attach效率高
} else {
fragment = getItem(position);//初始化頁面的時候拿到fragment的實例
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));//add上去
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);//并沒有真正釋放fragment對象只是detach
}
小結
?????從源碼中我們可以看出FragmentStatePagerAdapter中fragment實例在destroyItem的時候被真正釋放,所以FragmentStatePagerAdapter省內存。FragmentPagerAdapter中的fragment實例在destroyItem的時候并沒有真正釋放fragment對象只是detach,也就是說,FragmentPagerAdapter銷毀的是Fragment的視圖,而FragmentStatePagerAdapter銷毀的是實例對象,所以FragmentPagerAdapter消耗更多的內存,帶來的好處就是效率更高一些。所以得出這樣的結論:FragmentPagerAdapter適用于頁面比較少的情況,FragmentStatePagerAdapter適用于頁面比較多的情況,因此不同的場合選擇合適的適配器才是正確的做法