電子市場總結(二)

電子市場總結(二)

1、封裝一個LoadingPager

什麼是LoadingPager?

在項目中我們可以看到,大多數頁面都有五種情況發生

  1. 未加載
  2. 正在加載(轉圈的那種)
  3. 加載成功了
  4. 加載失敗了
  5. 加載成功但是沒有任何數據

可以想象,我們的項目中需要大量的去構建這樣的界面,如果我們簡單只是將每個狀態對應的頁面先寫完整,再在每一個Fragment中整合那樣的工作量很大,而且不易維護。

此時爲了適應這種大規模使用的現象,我們利用一個具備五種狀態的FrameLayout來簡化這些操作。

自定義LoadingPager的詳細解釋

1.自定義一個FrameLayout,命名爲LoadingPager

2.五種數據加載的狀態

public static final int 
    STATE_EMPTY = 0, // 未加載
    STATE_ERROR = -1, // 加載失敗
    STATE_SUCCESS = 1, // 加載成功
    STATE_LOADING = 2, // 正在加載(轉圈的那種)
    STATE_NO_DATA = -2; // 沒有加載到數據(加載成功的第二種狀態)

同時五種狀態對應五種佈局

private View mEmptyLayout, // 空佈局
    mErrorLayout, // 錯誤佈局
    mLoadLayout, // 加載中佈局
    mNoDataLayout, // 未加載到數據的佈局
    mSuccessLayout; //成功的佈局,由調用者自定義

3.加載成功的佈局

1.控制顯示

可以看出LoadingPager雖然可以封裝這些狀態,並且可以根據不同的狀態展示不同的佈局,但是這些佈局仍然可以自定義,並由調用者根據自己的需求更換對應狀態的佈局。
因此可以暴漏一些方法來讓調用者去自定義這些佈局。此處僅對成功的佈局進行了拓展,當我們的狀態變爲 STATE_SUCCESS 時,切換成調用者提供的佈局。代碼如下

/**
 * 展示當前的狀態對應的佈局,隱藏其他佈局
 */
private void showCurrentLayout() {
    mEmptyLayout.setVisibility(currentState == STATE_EMPTY ? VISIBLE : GONE);

    mErrorLayout.setVisibility(currentState == STATE_ERROR ? VISIBLE : GONE);

    mLoadLayout.setVisibility(currentState == STATE_LOADING ? VISIBLE : GONE);

    mNoDataLayout.setVisibility(currentState == STATE_NO_DATA ? VISIBLE : GONE);

    // 加載 數據成功 且是在佈局初次加載的狀態
    if (currentState == STATE_SUCCESS && mSuccessLayout == null) {
        mSuccessLayout = initSuccessLayout();
        // 所加載的佈局不能爲空
        if (mSuccessLayout != null)
            addView(mSuccessLayout);
    }
    // 當加載成功的佈局成功設置後,對其狀態進行判斷
    if (mSuccessLayout != null)
        mSuccessLayout.setVisibility(currentState == STATE_SUCCESS ? VISIBLE : GONE);
}

通過使用抽象方法的形式,強制要求調用者去實現加載成功的佈局

2.控制當前狀態

提供設置狀態的方法,讓調用者可以在自己想要的時間裏去展示成功的佈局

/**
 * 設置Loadingpager當前的狀態
 *
 */
public void setCurrentState(int state) {
    // 先判斷當前結果是否爲空
    this.currentState = state;
    showCurrentLayout();
}

3.重新加載的方法

暴漏一個可以設置重新加載事件的方法

public void setOnReloadClickListener(OnClickListener cl){
    btn_load_again.setOnClickListener(cl);
}

2、封裝一個BaseFragment

1.爲什麼還要封裝一個BaseFragment?

在前面我們已經封裝了一個LoadingPager,我們可以很方便的控制這個自定義view去設置當前的狀態和佈局。

那麼這個view應該在那裏顯示呢?view本身不能去控制當前的狀態,調用者控制其顯示的邏輯又在哪裏呢?

提出這兩個問題的同時,我們還要考慮,這麼多的fragment他們都要用到LoadingPager因此我們應將這個兩個問題的解決方法統一的封裝起來,在一個BaseFragment中去處理,其他想要使用這種LoadingPager模式的Fragment只要繼承BaseFragment就可以只關心自己的數據加載和界面展示了。

2.具體的封裝

1.onCreateView中去展示LoadingPager,同時要求子類實現initSuccessLayout()方法來設置加載成功的佈局,因爲BaseFragment雖然實現了LoadingPager,但是他仍然需要將這個問題交給子類。

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    //統一使用自定義的具備狀態的loadingpager
    mRootLayout = new LoadingPager(mActivity) {
        @Override
        public View initSuccessLayout() {
            mSuccessLayout = BaseFragment.this.initSuccessLayout();
            return mSuccessLayout;
        }
    };
    //設置重新加載的監聽器
    mRootLayout.setOnReloadClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            loading();
        }
    });

    return mRootLayout;
}

2.同時我們可以將Activity在BaseFragment中拿出來,這樣子類可以直接使用

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.mActivity = getActivity();
}

3.加載數據的邏輯

這裏考慮到所有的數據加載都需要IO處理或者是通過網絡來加載,那麼可以BaseFragment裏使用子線程這件事統一包辦了,子類只需要完成 loadData() 方法返回他的加載狀態即可。子類來返回加載狀態的原因是,BaseFragment並不知道是怎麼去加載的,所以這個邏輯仍然要拋給子類。

/**
 * BaseFragment統一具備的加載方法,子線程加載
 */
public void loading() {
    // 考慮到大多數的加載過程都是請求網絡,所以採用子線程
    ThreadManager.ThreadPool instance = ThreadManager.getInstance();
    instance.execute(new Runnable() {
        @Override
        public void run() {
            // 子類加載後返回對應的狀態,如何去控制顯示的問題父類來解決即可
            final BaseFragment.State state = loadData();
            // 設置loadingpager的狀態
            UIUtils.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (mRootLayout != null && state != null)
                        mRootLayout.setCurrentState(state.getState());
                }
            });
        }
    });
}

4.爲子類中對數據的加載成功與否提供一個默認的判斷

/**
 * 提供給子類進行檢查初次加載成功與否
 *
 */
protected State checkLoad(List list) {
    if (list != null) {
        if (list.size() == 0)
            return State.STATE_NO_DATA;
        else
            return State.STATE_SUCCESS;
    }
    return State.STATE_ERROR;
}

5.重新加載的邏輯,即對重新加載的按鈕設置點擊事件

//設置重新加載的監聽器
mRootLayout.setOnReloadClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        loading();
    }
});

6.總結,子類需要完成這些事情

  1. protected abstract View initSuccessLayout(); // 加載成功後是什麼樣的佈局
  2. protected abstract State loadData(); // 加載數據的邏輯
  3. protected abstract void initListener(); // 監聽的初始化

封裝完成後的效果如下:

3、其他的知識點

1.枚舉的用法

枚舉他實際相當於一個變相的class類,他可以設置構造器,但是他的主要目的還是定一些不會被改變的值,如下將LoadingPager對應的狀態進行了封裝,那麼子類在加載數據的時候就可以只返回枚舉值而不是int數值了:

/**
 * 枚舉的用法
 */
public static enum State {
    STATE_ERROR(LoadingPager.STATE_ERROR),
    STATE_LOADING(LoadingPager.STATE_LOADING),
    STATE_SUCCESS(LoadingPager.STATE_SUCCESS),
    STATE_NO_DATA(LoadingPager.STATE_NO_DATA);

    int state;
    // 枚舉的構造器實際上只能自己使用,可以通過這種方式,爲自己設置數據
    State(int state) {
        this.state = state;
    }

    // 獲取枚舉存儲的數據,調用者可以通過get方式去獲取這個數據
    int getState() {
        return state;
    }
}

4、封裝思想的總結

LoadingPager和BaseFragment的封裝抽取思想僅僅是整個項目的開始,其中LoadingPager是對佈局顯示的封裝,BaseFragment是對如何使用LoadingPager和控制LoadingPager的封裝。
通過這兩層封裝,子類所有集成自BaseFragment的對象只需要關心自己要顯示什麼和數據的加載即可。

在這裏並沒有涉及任何新的知識點,而是基本的封裝思想的搭建。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章