ViewPager防止Fragment銷毀以及取消Fragment的預加載
存在的問題
1. 默認情況下,ViewPager會根據setOffscreenPageLimit()方法設置的大小,自動預加載
2. 還是根據setOffscreenPageLimit()方法設置的大小,會去銷毀fragment視圖
下面的圖說明情況
滑動fragment1,此時會預加載fragment2,滑動到fragment2會預加載fragment3,但是滑動到fragment3,此時會調用fragment1的destroyview方法,銷毀視圖。當重新滑動到fragment1才會重新調用fragment1的oncreateview方法。注意此時并不會銷毀實例,不會調用ondestroy方法
這樣就存在兩個問題
1.pagerview頻繁切換,導致fragment1.fragment3在頻繁的調用destroyview和oncreateview方法,重新創建視圖。這樣也浪費了大量的資源,用戶體驗不佳,雖然內存消耗比較低
2.因為切換到fragment1的時候,同時預加載了fragment2,如果此時fragment2也有大量的耗時網絡請求要做,如果應用對啟動反應速度比較敏感,所以此時做了多余的工作。能否把這些耗時的工作延遲加載,也是個問題
解決方案
1. 防止頻繁的銷毀視圖,setOffscreenPageLimit(2)/或者重寫PagerAdaper的destroyItem方法為空即可
setOffscreenPageLimit(2)
//Set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.Pages beyond this limit will be recreated from the adapter when needed.You should keep this limit low, especially if your pages have complex layouts. This setting defaults to 1.
大概意思就是說:
設置當前page左右兩側應該被保持的page數量,超過這個限制,page會被銷毀重建(只是銷毀視圖),onDestroy-onCreateView,但不會執行onDestroy。盡量維持這個值小,特別是有復雜布局的時候,因為如果這個值很大,就會占用很多內存,如果只有3-4page的話,可以全部保持active,可以保持page切換的順滑
這下很好理解了,默認情況下是1,所以當前fragment左右兩側,就會被保持1頁pager,所以上述切換到fragment2并不會銷毀任何視圖,但是到fragment1,3會。這里注意這個值,是左右兩側能夠維持的page,所以如果setOffscreenPageLimit(2),那么就不會頻繁的銷毀了
destroyItem()
//super.destroyItem(Container, position, object); 注釋掉調用父類方法即可
2. 取消預加載,可以fragment的setUserVisibleHint實現,具體實現參考代碼示例
setUserVisibleHint
//Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.
大概意思就是:fragment對用戶可見,isVisibleToUser為true,不可見isVisibleToUser為false。對應于viewpager,當前pager,非當前pager
代碼:
public abstract class BaseFragment extends Fragment {
/** Fragment當前狀態是否可見 */
protected boolean isVisible;
//setUserVisibleHint adapter中的每個fragment切換的時候都會被調用,如果是切換到當前頁,那么isVisibleToUser==true,否則為false
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
isVisible = true;
onVisible();
} else {
isVisible = false;
onInvisible();
}
}
/**
* 可見
*/
protected void onVisible() {
lazyLoad();
}
/**
* 不可見
*/
protected void onInvisible() {
}
/**
* 延遲加載
* 子類必須重寫此方法
*/
protected abstract void lazyLoad();
}
public class CustomListFragment extends BaseFragment {
private Context context;
private static final String FRAGMENT_INDEX = "fragment_index";
private final int FIRST_FRAGMENT = 0;
private final int SECOND_FRAGMENT = 1;
private final int THIRD_FRAGMENT = 2;
private TextView contentText;
private ProgressBar progressBar;
private int mCurIndex = -1;
/**
* 標志位,標志已經初始化完成
*/
private boolean isPrepared;
/**
* 是否已被加載過一次,第二次就不再去請求數據了
*/
private boolean mHasLoadedOnce;
/**
* 創建新實例
*
* @param index
* @return
*/
public static CustomListFragment newInstance(int index) {
Bundle bundle = new Bundle();
bundle.putInt(FRAGMENT_INDEX, index);
CustomListFragment fragment = new CustomListFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getActivity();
Bundle bundle = getArguments();
if (bundle != null) {
mCurIndex = bundle.getInt(FRAGMENT_INDEX);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
contentText = (TextView) view.findViewById(R.id.content);
progressBar = (ProgressBar) view.findViewById(R.id.progressbar);
isPrepared = true;
lazyLoad();
return view;
}
@Override
protected void lazyLoad() {
if (!isPrepared || !isVisible || mHasLoadedOnce) {
return;
}
new AsyncTask<Void, Void, Boolean>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.sleep(2000);
//在這里添加調用接口獲取數據的代碼
//doSomething()
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
@Override
protected void onPostExecute(Boolean isSuccess) {
if (isSuccess) {
// 加載成功
setView();
mHasLoadedOnce = true;
} else {
// 加載失敗
}
progressBar.setVisibility(View.GONE);
}
}.execute();
}
private void setView() {
// 根據索引加載不同視圖
switch (mCurIndex) {
case FIRST_FRAGMENT:
contentText.setText("第一個");
break;
case SECOND_FRAGMENT:
contentText.setText("第二個");
break;
case THIRD_FRAGMENT:
contentText.setText("第三個");
break;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d("LiaBin", "onDestroyView: curIndex=" + mCurIndex);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("LiaBin", "onDestroy: curIndex=" + mCurIndex);
}
}
浙公網安備 33010602011771號