1. 程式人生 > >ViewPager懶載入(取消預載入)的實現

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初始化資料。