ViewPager懶載入(取消預載入)的實現
ViewPager大家經常用到的一個控制元件,經常會需要取消它的預載入功能。
下面貼一段程式碼可以完美實現懶載入功能,親測可用。
首先需要寫一個基類,所有需要懶載入的都繼承這個類。這裡只寫了一些重要方法的實現,其他基類中的方法根據自己的需求新增
/** * * ViewPager + Fragment 結構中 * ViewPager 有預載入功能,在訪問網路的時候會同時載入多個頁面的網路,體驗很不好, 更會影響一些帶有頁面進度條的顯示 * 所以ViewPager中的Fragment 都繼承這個類。 效果是隻預載入佈局,但是不會訪問網路。 */ public abstract class LazyBaseFragment extends Fragment { public View view; /** * Fragment當前狀態是否可見 */ protected boolean isVisible; /** * 初始化view物件,這裡在Fragment中的onCreateView方法中進行實現,返回一個View物件 */ public abstract View initView(); /** * 初始化資料 */ public abstract void initData(); //先於oncreatview執行的方法 @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (getUserVisibleHint()) { isVisible = true; onVisible(); } else { isVisible = false; onInvisible(); } } /** * 可見 */ protected void onVisible() { lazyLoad(); } /** * 不可見 */ protected void onInvisible() { } /** * 延遲載入 */ protected abstract void lazyLoad(); }
下面貼一段具體實現類的程式碼
/** * * 用於首頁Viewpager+Fragment中的 Fragment */ public class ViewPagerFragment extends LazyBaseFragment { private static final String tag = "ViewPagerFragment"; private static final int SUCCESS = 11; /** * 標誌位,標誌已經初始化完成 */ private boolean isPrepared; /** * 是否已被載入過一次,第二次就不再去請求資料了 */ private boolean mHasLoadedOnce; // 以上兩個變數至關重要,主要靠這兩個變數實現 @Bind(R.id.listView) PullToRefreshListView pullToRefreshListView; @Bind(R.id.layout_requsest_fail) LinearLayout layout_requsest_fail; @Bind(R.id.butTryAgain) Button butTryAgain; int page = 1; String type = ""; private ViewPagerAdp adp; private PageDialog pageDialog; public ViewPagerFragment(int type) { this.type = type + ""; } public ViewPagerFragment() { } //這裡的程式碼是一兩年前的,當時網路底層還是用handler進行傳輸資料, // 這裡就是實現網路資料請求結果的程式碼。在請求到正確的結果以後,記得將 //mHasLoadedOnce 置為true 。 @Override protected void handleMsg(Message msg) { switch (msg.what) { case Constant.NETWORK_MEG_WHAT_FAILURE_1: pullToRefreshListView.onRefreshComplete(); if(null!= pageDialog){ pageDialog.dismiss(); } Bundle b1 = msg.getData(); if(null!=b1){ String failureMsg = b1.getString(Constant.NETWORK_FAILURE); } layout_requsest_fail.setVisibility(View.GONE); ToastUtlis.showShort(getActivity(), failureMsg); break; case Constant.NETWORK_MEG_WHAT_ERROR_2: // 網路錯誤 pullToRefreshListView.onRefreshComplete(); if(null!= pageDialog){ pageDialog.dismiss(); } layout_requsest_fail.setVisibility(View.VISIBLE); ToastUtlis.showShort(getActivity(), getResources().getString(R.string.network_error)); break; case SUCCESS: if (mHasLoadedOnce != true) { mHasLoadedOnce = true; } if(null!= pageDialog){ pageDialog.dismiss(); } pullToRefreshListView.onRefreshComplete(); layout_requsest_fail.setVisibility(View.GONE); String data = msg.getData().getString(Constant.NETWORK_SUCCESS); LogUtils.i("網路", "成功" + msg.getData().getString(Constant.NETWORK_SUCCESS)); //Gson解析資料 parseListData(data); break; default: break; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { initView();//初始化檢視 // 頁面初始化已經完成 變數置為true isPrepared = true; initData(); ViewGroup parent = (ViewGroup) view.getParent(); if (parent != null) { parent.removeView(view); } return view; } @Override public View initView() { //此處view為基類中的view,所以方法最後返回了null view = View.inflate(getActivity(), R.layout.fragment_viewpagerfragment, null); pageDialog = new PageDialog(getActivity());//頁面進度條 ButterKnife.bind(this, view); return null; } /** *初始化資料的方法。 注意: 當請求網路成功以後, 一定要將 mHasLoadedOnce 置為true !!! */ @Override public void initData() { if (!isPrepared || !isVisible || mHasLoadedOnce) { return; } requestNet(); //發起網路請求的方法 } private void requestNet() { // 這個方法裡面是發起網路請求的具體實現, pageDialog是專案中使用的頁面進度條,這個可以忽略 pageDialog.show(); // } public void parseListData(String result) { //解析資料 } // 這裡要特別注意,這個lazyLoad一定要重寫,並且裡面實現initData方法。 //這裡因為程式碼實現非常少,所以經常被人忽略,特此提醒 @Override protected void lazyLoad() { initData(); } }
因為程式碼是之前的程式碼,刪掉了一些沒用的,這樣看起來清晰一點。下面解釋一下流程和用意。
在基類中 setUserVisibleHint()這個方法是先於fragment初始化方法oncreateView執行的,如果當前頁面在顯示中,getUserVisibleHint()就為true,否則為false。
viewpager預載入時,頁面不顯示,isVisible為false。
然後開始對fragment的預載入過程,在fragment的oncreateview執行時,執行到initData時 ,正常的操作在這裡應該請求網路資料了,但是因為在基類setUserVisibleHint中將isVisible置為false。所以initData沒到請求網路時就return了,只完成了頁面佈局的初始化,沒有請求網路。
在滑動到fragment時,setUserVisibleHint中isVisible為true,並且呼叫lazyLoad(注意,此時fragment的oncreateview早已執行過,並且只會執行一次, 所以一定要在實現類中的lazyLoad實現initData方法,不然只是個空方法,不會載入資料),實現類中lazyLoad的具體實現為資料的初始化,此時,頁面佈局已經完成初始化,當前頁面也在顯示中,並且資料也沒有載入過,三個變數的條件都滿足, 去執行資料載入的過程(接收intent或者請求網路資料等)。
由此實現了整個過程,在預載入時只執行了initview初始化了佈局,然後在頁面顯示的時候去執行initData初始化資料。