[38→100]一個常規(guī)的Android頁面模型:NetFragment

問題場景

Android開發(fā)中大部分頁面都包含如下邏輯:

  1. 進(jìn)入頁面钞螟,加載網(wǎng)絡(luò)數(shù)據(jù)勤众,顯示加載頁面
  2. 加載完成后论寨,有以下幾種情況:
  3. 正常返回赖临,有有效數(shù)據(jù)胞锰,顯示“有效界面
  4. 正常返回,有效數(shù)據(jù)為空兢榨,顯示“空數(shù)據(jù)界面
  5. 正常返回嗅榕,服務(wù)器判定輸入?yún)?shù)異常顺饮,顯示“參數(shù)異常界面”,比如“用戶登錄失效”等凌那,直接跳轉(zhuǎn)相應(yīng)的頁面
  6. 異常返回兼雄,網(wǎng)絡(luò)超時,無法訪問服務(wù)器等狀態(tài)案怯,顯示“加載失敗頁面
  7. 無法訪問網(wǎng)絡(luò)君旦,顯示“無法訪問網(wǎng)絡(luò)界面”。

怎么解決呢嘲碱?

原本想各個界面由NetFragment提供接口進(jìn)行設(shè)置金砍,后來考慮Android的界面基本xml繪制,透過id與代碼綁定麦锯。那不如采用“約定優(yōu)于配置原則”恕稠,直接規(guī)定好那些id標(biāo)識哪些界面,NetFragment里面根據(jù)相應(yīng)的id去尋找界面即可扶欣。所以最后約定id名稱如下:

  • 加載界面 R.id.net_progress
  • 有效界面 R.id.net_result
  • 空數(shù)據(jù)界面 R.id.net_no_result
  • 參數(shù)異常界面 R.id.net_error
  • 加載失敗頁面 R.id.net_fail
  • 無法訪問網(wǎng)絡(luò)界面 R.id.net_cannot_access

基于SOLID原則中的S——單一責(zé)任原則(The Single Responsibility Principle)鹅巍,各個界面的點擊、觸摸都由各自處理料祠,NetFragment只負(fù)責(zé)加載數(shù)據(jù)骆捧、控制各類界面顯示隱藏,至于下拉刷新髓绽、上拉加載下一頁數(shù)據(jù)等操作都不在此類里面內(nèi)進(jìn)行管理敛苇。

最后產(chǎn)生的代碼——NetFragment.java

package lib;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.freetek.deepsea.R;

import panda.android.lib.base.control.SimpleSafeTask;
import panda.android.lib.base.model.ListNetResultInfo;
import panda.android.lib.base.model.NetResultInfo;
import panda.android.lib.base.model.net.BaseRepositoryCollection;
import panda.android.lib.base.ui.fragment.BaseFragment;
import panda.android.lib.base.util.DevUtil;
import panda.android.lib.base.util.Log;

/**
 * 加載網(wǎng)頁數(shù)據(jù)的通用模型,包含 進(jìn)度條控制顺呕、結(jié)果顯示控制枫攀。
 *
 * @param <T>
 * @author shitianci
 */
public abstract class NetFragment<T extends NetResultInfo> extends BaseFragment {

    private static final String TAG = NetFragment.class.getSimpleName();
    protected View mViewProgress = null;  //加載界面
    protected View mViewResult = null; //有效界面
    protected View mViewNoResult = null; //空數(shù)據(jù)界面
    protected View mViewError = null; //參數(shù)異常界面
    protected View mViewFail = null; //加載失敗頁面
    protected View mViewCannotAccess = null; //無法訪問網(wǎng)絡(luò)

    protected SimpleSafeTask<T> netTask = null;
    private View[] mViewArray;
    private int[] mViewArrayID = new int[]{
            R.id.net_progress, //加載界面
            R.id.net_result, //有效界面
            R.id.net_no_result, //空數(shù)據(jù)界面
            R.id.net_error, //參數(shù)異常界面
            R.id.net_fail, //加載失敗頁面
            R.id.net_cannot_access}; //無法訪問網(wǎng)絡(luò)

    //todo 這些數(shù)據(jù)需要放到xml里面進(jìn)行管理
    private String[] mViewToastString = new String[]{
            "正在加載中", //加載界面
            "加載到有效數(shù)據(jù)了", //有效界面
            "加載數(shù)據(jù)為空", //空數(shù)據(jù)界面
            "參數(shù)異常", //參數(shù)異常界面
            "無法訪問服務(wù)器", //加載失敗頁面
            "無法訪問網(wǎng)絡(luò)"}; //無法訪問網(wǎng)絡(luò)


    @Override
    public int getLayoutId() {
        return R.layout.panda_fragment_net;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View createdView = super.onCreateView(inflater, container,
                savedInstanceState);
        mViewProgress = createdView.findViewById(R.id.net_progress);
        mViewResult = createdView.findViewById(R.id.net_result);
        mViewNoResult = createdView.findViewById(R.id.net_no_result);
        mViewError = createdView.findViewById(R.id.net_error);
        mViewFail = createdView.findViewById(R.id.net_fail);
        mViewCannotAccess = createdView.findViewById(R.id.net_cannot_access);
        mViewArray = new View[]{mViewProgress, mViewResult, mViewNoResult, mViewError, mViewFail, mViewCannotAccess};

        return createdView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        loadNetData(false);
    }

    boolean loadingNetData = false;

    public void loadNetData() {
        loadNetData(false);
    }

    public void loadNetData(final boolean useSecondModel) {
        Log.d(TAG, "loadNetData, loadingNetData = " + loadingNetData);
        if (loadingNetData) {
            return;
        }
        if (!BaseRepositoryCollection.tryToDetectNetwork(getActivity())) {
            showOnlyView(R.id.net_cannot_access, useSecondModel);
            return;
        }
        netTask = new SimpleSafeTask<T>(getActivity()) {

            protected void onPreExecuteSafely() throws Exception {
                loadingNetData = true;
                showOnlyView(R.id.net_progress, useSecondModel);
                onPreloadNetData(useSecondModel);
            }

            @Override
            protected T doInBackgroundSafely() throws Exception {
                T result = onDoInBackgroundSafely();
                return result;
            }

            @Override
            protected void onPostExecuteSafely(T result, Exception e) {
                onPostloadNetData(useSecondModel);
                super.onPostExecuteSafely(result, e);
                loadingNetData = false;
                if (e != null || result == null) {
                    showOnlyView(R.id.net_fail, useSecondModel);
                    return;
                }
                if (result.getRespCode() != NetResultInfo.RETURN_CODE_000000) {
                    showOnlyView(R.id.net_error, useSecondModel);
                    return;
                }
                if (result instanceof ListNetResultInfo && ((ListNetResultInfo) result).getList().size() == 0) {
                    showOnlyView(R.id.net_no_result, useSecondModel);
                    return;
                }
                showOnlyView(R.id.net_result, useSecondModel);
                onDisplayResult(result);
            }

            protected void onCancelled() {
                loadingNetData = false;
                showOnlyView(R.id.net_fail, useSecondModel);
            }


        };
        netTask.execute();
        return;
    }

    /**
     * -------------------------
     * START: 最重要的流程方法
     * -------------------------
     */


    /**
     * 開始加載網(wǎng)絡(luò)數(shù)據(jù)
     * @param useSecondModel
     */
    public void onPreloadNetData(boolean useSecondModel) {
    }


    /**
     * 加載后臺數(shù)據(jù)
     *
     * @return
     */
    protected abstract T onDoInBackgroundSafely();


    /**
     * 顯示后臺數(shù)據(jù)
     *
     * @param result
     */
    protected abstract void onDisplayResult(T result);

    /**
     * 網(wǎng)絡(luò)數(shù)據(jù)加載結(jié)束
     * @param useSecondModel
     */
    public void onPostloadNetData(boolean useSecondModel) {

    }

    /**
     * -------------------------
     * END
     * -------------------------
     */


    /**
     * -------------------------
     * START: 純粹的界面展示控制,不推薦外部調(diào)用株茶、重寫
     * -------------------------
     */

    /**
     * 顯示指定的View来涨,剩下的界面都隱藏。
     *
     * @param useSecondModel 是否啟用第二次模式启盛,在加載第2……n次數(shù)據(jù)時蹦掐,無需切換其它界面,采用Toast顯示信息即可僵闯。
     */
    protected void showOnlyView(int viewId, boolean useSecondModel) {
        int i = 0;
        for (View v : mViewArray) {
            i++;
            if (v == null) {
                continue;
            } else if (v.getId() == viewId) {
                Log.d(TAG, "showOnlyView, i = " + i);
                if (useSecondModel && (viewId != R.id.net_progress || viewId != R.id.net_result)) {
                    DevUtil.showInfo(getActivity(), mViewToastString[i-1]);
                } else {
                    v.setVisibility(View.VISIBLE);
                }

            } else {
                v.setVisibility(View.GONE);
            }
        }
    }

    protected void showProgress() {
        Log.d(TAG, "showProgress, mViewProgress = " + mViewProgress);
        if (mViewProgress != null) {
            mViewProgress.setVisibility(View.VISIBLE);
        }
    }

    protected void hiddenProgress() {
        Log.d(TAG, "hiddenProgress, mViewProgress = " + mViewProgress);
        if (mViewProgress != null) {
            mViewProgress.setVisibility(View.GONE);
        }
    }

//    protected void showResult() {
//        Log.d(TAG, "showResult");
//        if (mViewResult != null) {
//            mViewResult.setVisibility(View.VISIBLE);
//        }
//    }
//
//    protected void hiddenResult() {
//        Log.d(TAG, "hiddenResult");
//        if (mViewResult != null) {
//            mViewResult.setVisibility(View.GONE);
//        }
//    }
//
//    protected void showNoResult() {
//        Log.d(TAG, "showNoResult");
//        if (mViewNoResult != null) {
//            mViewNoResult.setVisibility(View.VISIBLE);
//        }
//    }
//
//    protected void hiddenNoResult() {
//        Log.d(TAG, "hiddenNoResult");
//        if (mViewNoResult != null) {
//            mViewNoResult.setVisibility(View.GONE);
//        }
//    }
//
//    public void showError() {
//        Log.d(TAG, "showNetErrResult");
//        if (mViewError != null) {
//            mViewError.setVisibility(View.VISIBLE);
//        }
//    }
//
//    private void hiddenError() {
//        if (mViewError != null) {
//            mViewError.setVisibility(View.GONE);
//        }
//    }
//
//    public void showFail() {
//        Log.d(TAG, "showFail");
//        if (mViewFail != null) {
//            mViewFail.setVisibility(View.VISIBLE);
//        }
//    }
//
//    public void hiddenFail() {
//        Log.d(TAG, "hiddenFail");
//        if (mViewFail != null) {
//            mViewFail.setVisibility(View.GONE);
//        }
//    }
//
//    public void showCannotAccess() {
//        if (mViewCannotAccess != null) {
//            mViewCannotAccess.setVisibility(View.VISIBLE);
//        }
//    }
//
//    public void hiddenCannotAccess() {
//        if (mViewCannotAccess != null) {
//            mViewCannotAccess.setVisibility(View.GONE);
//        }
//    }

    /**
     * -------------------------
     * END
     * -------------------------
     */

}

后續(xù)拓展

可以從開發(fā)者使用角度考慮笤闯,內(nèi)置一系列子類,達(dá)到各種場景中“One Step”集成的效果棍厂。比如默認(rèn)效果為:

  • 加載界面:彈出一個透明背景的加載框
  • 有效界面:支持 下拉刷新颗味、上拉加載下一頁
  • 空數(shù)據(jù)界面:支持圖文格式
  • 參數(shù)異常界面:采用Toast提示
  • 加載失敗頁面:提醒用戶網(wǎng)速過慢,建議切換網(wǎng)絡(luò)
  • 無法訪問網(wǎng)絡(luò)界面:提醒用戶打開網(wǎng)絡(luò)

Panda
2016-06-03

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末牺弹,一起剝皮案震驚了整個濱河市浦马,隨后出現(xiàn)的幾起案子时呀,更是在濱河造成了極大的恐慌,老刑警劉巖晶默,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谨娜,死亡現(xiàn)場離奇詭異,居然都是意外死亡磺陡,警方通過查閱死者的電腦和手機趴梢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來币他,“玉大人坞靶,你說我怎么就攤上這事『ぃ” “怎么了彰阴?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長拍冠。 經(jīng)常有香客問我尿这,道長,這世上最難降的妖魔是什么庆杜? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任射众,我火速辦了婚禮,結(jié)果婚禮上晃财,老公的妹妹穿的比我還像新娘叨橱。我一直安慰自己,他們只是感情好拓劝,可當(dāng)我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布雏逾。 她就那樣靜靜地躺著嘉裤,像睡著了一般郑临。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上屑宠,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天厢洞,我揣著相機與錄音,去河邊找鬼典奉。 笑死躺翻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的卫玖。 我是一名探鬼主播公你,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼假瞬!你這毒婦竟也來了陕靠?” 一聲冷哼從身側(cè)響起迂尝,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剪芥,沒想到半個月后垄开,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡税肪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年溉躲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片益兄。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡锻梳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出偏塞,到底是詐尸還是另有隱情唱蒸,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布灸叼,位于F島的核電站神汹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏古今。R本人自食惡果不足惜屁魏,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望捉腥。 院中可真熱鬧氓拼,春花似錦、人聲如沸抵碟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拟逮。三九已至撬统,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間敦迄,已是汗流浹背恋追。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罚屋,地道東北人苦囱。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像脾猛,于是被迫代替她去往敵國和親撕彤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,749評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理猛拴,服務(wù)發(fā)現(xiàn)羹铅,斷路器瞧柔,智...
    卡卡羅2017閱讀 134,629評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法睦裳,內(nèi)部類的語法造锅,繼承相關(guān)的語法,異常的語法廉邑,線程的語...
    子非魚_t_閱讀 31,598評論 18 399
  • 《盼西歸》 東起盼西歸哥蔚,空了色達(dá)階。 遙望吾師榻蛛蒙,何年再叩首糙箍。 何一鷗/17.4.22
    土登根珠閱讀 334評論 0 0
  • 口才的好壞,最基本的還在于多練習(xí)牵祟,那么練習(xí)以那為主哪深夯?其實要想脫口成章表達(dá)的流利就是腦和嘴的協(xié)調(diào)能力,沒有思路也就...
    佰荷閱讀 250評論 0 0