ViewPager+Fragment使用中的幾個常見問題總結
1.實現迴圈切換
思路一:在ViewPager的Adapter中返回count的值為 Integer.MAX_VALUE ,進行初始化的時候講ViewPager的 setCurrentItem(int item) 的方法中傳入Integer.MAX_VALUE的一箇中間值,因為Int的最大值是2147483647 如果設它的中間值使用者是很難滑到兩端的,但是並意味著不能滑到兩端。
思路二:在item的兩端各增加一個Item,當ViewPager滑動到第一個Item的時候跳轉到倒數第二Item,當滑動到最後一個Item時候跳轉到第二個Item,設定跳轉的方法一定要用
public void setCurrentItem(int item, boolean smoothScroll)
傳遞引數為false ,這樣跳轉的就會很流暢。
部分實現程式碼
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { removeCheck(); Log.i(TAG, "onPageScrolled: positionOffset = "+positionOffset+ ", positionOffsetPixels = "+positionOffsetPixels); } @Override public void onPageSelected(int position) { RceLog.i(TAG, "onPageSelected() position : " + position); if (position == 0) { isChange = true; tabIndex = moduleList.size() - 2; } else if (position == moduleList.size() - 1) { isChange = true; tabIndex = 1; } else { tabIndex = position; } } @Override public void onPageScrollStateChanged(int state) { Log.i(TAG, "onPageScrollStateChanged: state = "+state); if (state == ViewPager.SCROLL_STATE_IDLE) { if (isChange) { isChange = false; viewPager.setCurrentItem(tabIndex, false); } } }
2.禁止預載入問題
看了一下網上的解決方案,不少人都建議使用ViewPager的setOffscreenPageLimit(int limit),但是通過檢視原始碼發現要禁止預載入使用該方法是無效的。
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
public void setOffscreenPageLimit(int limit) { if (limit < DEFAULT_OFFSCREEN_PAGES) { Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " + DEFAULT_OFFSCREEN_PAGES); limit = DEFAULT_OFFSCREEN_PAGES; } if (limit != mOffscreenPageLimit) { mOffscreenPageLimit = limit; populate(); } }
從上面的原始碼可以明顯的看出,使用該方法根本行不通,通過他設定預載入介面至少為一個介面,所以怎麼設定都會預載入的。
方案二:
在Fagment可見的時候在進行載入資料,重寫setUserVisibleHint(boolean isVisibleToUser)方法 進行判斷當前Fragment是否可見
public abstract class BaseFragment extends Fragment {
protected boolean isUserVisible;
protected boolean isPrepared;//標誌已經初始化完成
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (getUserVisibleHint()) {
isUserVisible = true;
onUserVisible();
} else {
isUserVisible = false;
onUserInvisible();
}
}
/**
* fragment 可見
*/
protected void onUserVisible() {
if (isPrepared && isUserVisible && isAdded()) {
update();
}
}
/**
* fragment 不可見
*/
protected void onUserInvisible() {
}
/**
* 子類重寫此方法,在fragment 可見的時候更新 UI
*/
protected abstract void update();
}
子類在重寫update方法,在update中進行資料更新
3.FragmentPagerAdapter與FragmentStatePagerAdapter
FragmentPagerAdapter:對於不再需要的fragment,選擇呼叫detach方法,僅銷燬檢視,並不會銷燬fragment例項。再次載入的時候時呼叫attach方法
destroyItem 的處理
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
this.mCurTransaction.detach((Fragment)object);
}
再次載入時instantiateItem 中的部分程式碼
Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
this.mCurTransaction.attach(fragment);
} else {
fragment = this.getItem(position);
this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
}
FragmentStatePagerAdapter:會呼叫remove方法銷燬不再需要的fragment,噹噹前事務提交以後,會徹底的將fragmeng從當前Activity的FragmentManager中移除,state標明,銷燬時,會將其onSaveInstanceState(Bundle outState)中的bundle資訊儲存下來,當用戶切換回來,可以通過該bundle恢復生成新的fragment,也就是說,你可以在onSaveInstanceState(Bundle outState)方法中儲存一些資料,在onCreate中進行恢復建立。
destroyItem 的處理:呼叫FragmentTransaction的remove方法從FragmentManager中移除Fragment,同時在mSaveState中儲存了Fragment的狀態。
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Fragment fragment = (Fragment)object;
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
while(this.mSavedState.size() <= position) {
this.mSavedState.add((Object)null);
}
this.mSavedState.set(position, fragment.isAdded() ? this.mFragmentManager.saveFragmentInstanceState(fragment) : null);
this.mFragments.set(position, (Object)null);
this.mCurTransaction.remove(fragment);
}
再次載入的時候回從mSavedState獲取狀態,設定給Fragment
instantiateItem(@NonNull ViewGroup container, int position) 中的部分程式碼
fragment = this.getItem(position);
if (this.mSavedState.size() > position) {
SavedState fss = (SavedState)this.mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
通過兩個Adapter的對比FragmentPagerAdapter沒一個Fragment都會儲存在記憶體中,因此使用一些頁面較少的情況,如果介面比較多的情況應該使用FragmentStatePagerAdapter。