轉載請註明出處:http://blog.csdn.net/linglongxin24/article/details/53205878
本文出自【DylanAndroid的博客】
Android中ViewPager+Fragment取消(禁止)預加載延遲加載(懶加載)問題解決方案
在Android中我們經常會用到ViewPager+Fragment組合。然而,有一個很讓人頭疼的問題就是,我們去加載數據的時候由於ViewPager的內部機制所限制,所以它會默認至少預加載一個。這讓人很鬱悶,所以,我就想到要封裝一個Fragment來解決這個問題。
這裏還解決一個問題就是在Android酷炫歡迎頁播放視頻,仿螞蜂窩自由行和慕課網
這裏感謝有一位網友提出了bug,就是在播放視頻的時候如果滑動到第二頁和第三頁,第一頁的視頻還在播放,這是個讓人很頭疼的問題,在這裏也完美解決。
1.問題初探
文章開始已經說過ViewPager的預加載機制。那麼,我們可不可以設置ViewPager的預加載爲0,不就解決問題了嗎?
vp.setOffscreenPageLimit(0);
經過測試發現,根本不是這麼回事,爲什麼呢?我們來看看Viewpager的setOffscreenPageLimit()方法的源碼
private static final int DEFAULT_OFFSCREEN_PAGES = 1;
/**
* 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.
*
* <p>This is offered as an optimization. If you know in advance the number
* of pages you will need to support or have lazy-loading mechanisms in place
* on your pages, tweaking this setting can have benefits in perceived smoothness
* of paging animations and interaction. If you have a small number of pages (3-4)
* that you can keep active all at once, less time will be spent in layout for
* newly created view subtrees as the user pages back and forth.</p>
*
* <p>You should keep this limit low, especially if your pages have complex layouts.
* This setting defaults to 1.</p>
*
* @param limit How many pages will be kept offscreen in an idle state.
*/
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();
}
}
我們發現,即使你設置爲0,那麼還是會在裏面判斷後設爲默認值1。所以這個方法是行不通的。
2.問題再探
我們發現Fragment中有一個setUserVisibleHint(boolean isVisibleToUser)方法,這個方法就是告訴用戶,UI對用戶是否可見,那麼我們在這裏去加載數據會怎麼樣呢?
這又是爲什麼呢?
因爲ViewPager會加載好多Fragment,爲了節省內容等會在Fragment不可見的某個時候調用onDestroyView()將用戶界面銷燬掉但是Fragment的實例還在,所以可能第一次加載沒有問題,
但是再次回到第一個Fragment再去加載的時候就會出現UI對用戶可見但是視圖還沒有初始化。
3.最終解決方案
package cn.bluemobi.dylan.viewpagerfragmentlazyload;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
/**
* Fragment預加載問題的解決方案:
* 1.可以懶加載的Fragment
* 2.切換到其他頁面時停止加載數據(可選)
* Created by yuandl on 2016-11-17.
* blog :http://blog.csdn.net/linglongxin24/article/details/53205878
*/
public abstract class LazyLoadFragment extends Fragment {
/**
* 視圖是否已經初初始化
*/
protected boolean isInit = false;
protected boolean isLoad = false;
protected final String TAG = "LazyLoadFragment";
private View view;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
view = inflater.inflate(setContentView(), container, false);
isInit = true;
/**初始化的時候去加載數據**/
isCanLoadData();
return view;
}
/**
* 視圖是否已經對用戶可見,系統的方法
*/
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isCanLoadData();
}
/**
* 是否可以加載數據
* 可以加載數據的條件:
* 1.視圖已經初始化
* 2.視圖對用戶可見
*/
private void isCanLoadData() {
if (!isInit) {
return;
}
if (getUserVisibleHint()) {
lazyLoad();
isLoad = true;
} else {
if (isLoad) {
stopLoad();
}
}
}
/**
* 視圖銷燬的時候講Fragment是否初始化的狀態變爲false
*/
@Override
public void onDestroyView() {
super.onDestroyView();
isInit = false;
isLoad = false;
}
protected void showToast(String message) {
if (!TextUtils.isEmpty(message)) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
}
/**
* 設置Fragment要顯示的佈局
*
* @return 佈局的layoutId
*/
protected abstract int setContentView();
/**
* 獲取設置的佈局
*
* @return
*/
protected View getContentView() {
return view;
}
/**
* 找出對應的控件
*
* @param id
* @param <T>
* @return
*/
protected <T extends View> T findViewById(int id) {
return (T) getContentView().findViewById(id);
}
/**
* 當視圖初始化並且對用戶可見的時候去真正的加載數據
*/
protected abstract void lazyLoad();
/**
* 當視圖已經對用戶不可見並且加載過數據,如果需要在切換到其他頁面時停止加載數據,可以覆寫此方法
*/
protected void stopLoad() {
}
}
}
4.用法
LazyLoadFragment是一個抽象類,可以作爲BaseFragment,繼承它。
(1).用setContentView()方法去加載要顯示的佈局
(2).lazyLoad()方法去加載數據
(3).stopLoad()方法可選,當視圖已經對用戶不可見並且加載過數據,如果需要在切換到其他頁面時停止加載數據,可以覆寫此方法
package cn.bluemobi.dylan.viewpagerfragmentlazyload; import android.util.Log; /** * Created by yuandl on 2016-11-17. */ public class Fragment1 extends LazyLoadFragment { @Override public int setContentView() { return R.layout.fm_layout1; } @Override protected void lazyLoad() { String message = "Fragment1" + (isInit ? "已經初始並已經顯示給用戶可以加載數據" : "沒有初始化不能加載數據")+">>>>>>>>>>>>>>>>>>>"; showToast(message); Log.d(TAG, message); } @Override protected void stopLoad() { Log.d(TAG, "Fragment1" + "已經對用戶不可見,可以停止加載數據"); } }
5.看效果界面