NetworkStateView:界面多狀態(tài)加載

在項(xiàng)目中經(jīng)常需要進(jìn)行不同狀態(tài)的加載铁瞒,例如在網(wǎng)絡(luò)請(qǐng)求時(shí)的加載中狀態(tài),加載失敗狀態(tài)桅滋,沒有網(wǎng)絡(luò)狀態(tài)和沒有數(shù)據(jù)的狀態(tài)等慧耍,之前在項(xiàng)目中的做法是把幾個(gè)不同的狀態(tài)布局都添加到需要進(jìn)行狀態(tài)切換的Activity或Fragment的布局文件當(dāng)中,接著再對(duì)每一個(gè)狀態(tài)界面進(jìn)行相應(yīng)的隱藏顯示丐谋,但是在界面一多的情況下芍碧,重復(fù)操作就會(huì)顯得很繁瑣。

在進(jìn)行了無數(shù)次這樣繁瑣的操作后号俐,有些受不了了泌豆,就想著能不能把這幾種狀態(tài)都封裝到同一個(gè)View中,在需要顯示不同的狀態(tài)時(shí)只需要調(diào)用相應(yīng)的狀態(tài)方法就可以進(jìn)行切換萧落,這樣可比上一種方法簡(jiǎn)便得多践美,哈哈洗贰,這當(dāng)然是可以的,接下來就介紹一下NetworkStateView陨倡。

NetworkStateView繼承自LinearLayout敛滋,在里面定義了加載成功,加載中兴革,加載出錯(cuò)(這里只統(tǒng)一定義為網(wǎng)絡(luò)出錯(cuò)绎晃,當(dāng)然了用在哪種出錯(cuò)方式上可以由你自己決定),沒有網(wǎng)絡(luò)杂曲,沒有數(shù)據(jù)五種狀態(tài)庶艾,其中加載成功表示用來顯示Activity或Fragment的界面,并用變量mCurrentState來記住當(dāng)前顯示的狀態(tài)擎勘,相應(yīng)的變量值如下:

//當(dāng)前的加載狀態(tài)
private int mCurrentState;
private static final int STATE_SUCCESS = 0;
private static final int STATE_LOADING = 1;
private static final int STATE_NETWORK_ERROR = 2;
private static final int STATE_NO_NETWORK = 3;
private static final int STATE_EMPTY = 4;

接著需要自定義屬性咱揍,用于傳入對(duì)應(yīng)的狀態(tài)布局文件,在同一種狀態(tài)中如果需要有不同的界面顯示棚饵,便可以對(duì)應(yīng)的傳入layout文件煤裙,這樣可以方便擴(kuò)展

<declare-styleable name="NetworkStateView">

    <!-- 加載中的布局id -->
    <attr name="loadingView" format="reference" />

    <!-- 加載錯(cuò)誤的布局id -->
    <attr name="errorView" format="reference" />
    <!-- 加載錯(cuò)誤的布局圖片 -->
    <attr name="nsvErrorImage" format="reference" />
    <!-- 加載錯(cuò)誤的布局文字 -->
    <attr name="nsvErrorText" format="string" />

    <!-- 沒有數(shù)據(jù)的布局id -->
    <attr name="emptyView" format="reference" />
    <!-- 沒有數(shù)據(jù)的布局圖片 -->
    <attr name="nsvEmptyImage" format="reference" />
    <!-- 沒有數(shù)據(jù)的布局文字 -->
    <attr name="nsvEmptyText" format="string" />

    <!-- 沒有網(wǎng)絡(luò)的布局id -->
    <attr name="noNetworkView" format="reference" />
    <!-- 沒有數(shù)據(jù)的布局圖片 -->
    <attr name="nsvNoNetworkImage" format="reference" />
    <!-- 沒有數(shù)據(jù)的布局文字 -->
    <attr name="nsvNoNetworkText" format="string" />

    <!-- 刷新的ImageView圖片id -->
    <attr name="nsvRefreshImage" format="reference"/>

    <!-- 文字大小 -->
    <attr name="nsvTextSize" format="dimension" />
    <!-- 文字顏色 -->
    <attr name="nsvTextColor" format="color" />
</declare-styleable>

定義了屬性之后,需要在NetworkStateView的構(gòu)造函數(shù)中使用TypedArray進(jìn)行相應(yīng)屬性值的查找噪漾,注意硼砰,這里查找得到時(shí)布局文件的id,最后在顯示的時(shí)候需要對(duì)布局文件id進(jìn)行相應(yīng)的inflate欣硼,查找之后可以進(jìn)行一些基本屬性的設(shè)置题翰,例如LayoutParamsBackgroundColor

public NetworkStateView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NetworkStateView, defStyleAttr, 0);
    mLoadingViewId = typedArray.getResourceId(R.styleable.NetworkStateView_loadingView, R.layout.view_loading);
    mErrorViewId = typedArray.getResourceId(R.styleable.NetworkStateView_errorView, R.layout.view_network_error);
    mNoNetworkViewId = typedArray.getResourceId(R.styleable.NetworkStateView_noNetworkView, R.layout.view_no_network);
    mEmptyViewId = typedArray.getResourceId(R.styleable.NetworkStateView_emptyView, R.layout.view_empty);

   ....

    typedArray.recycle();

    mInflater = LayoutInflater.from(context);
    params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    setBackgroundColor(getResources().getColor(R.color.white));
}

在使用屬性時(shí),可以直接在NetworkStateView的布局文件中進(jìn)行設(shè)置诈胜,又或者在styles文件中進(jìn)行設(shè)置

  • 直接在布局文件中聲明

      <com.zht.networkstateview.ui.widget.NetworkStateView xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:id="@+id/nsv_state_view"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_centerInParent="true"
          android:orientation="vertical"
          android:visibility="visible"
          app:emptyView="@layout/view_empty"
          app:errorView="@layout/view_network_error"
          app:loadingView="@layout/view_loading"
          app:noNetworkView="@layout/view_no_network"
          app:nsvTextColor="@color/gray_text_default"
          app:nsvTextSize="16sp">
      
      </com.zht.networkstateview.ui.widget.NetworkStateView>
    
  • 在styles文件進(jìn)行設(shè)置

      <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
          <!-- Customize your theme here. -->
          <item name="styleNetworkStateView">@style/NetworkStateViewTheme</item>
          ...
      </style>
    
      <style name="NetworkStateViewTheme" parent="NetworkStateView.Style">
          <item name="nsvTextSize">16sp</item>
          <item name="nsvTextColor">#ffffff</item>
          ...
      </style>
    

進(jìn)行上面的步驟后豹障,就可以加載相應(yīng)的布局文件了,以加載失敗(網(wǎng)絡(luò)出錯(cuò))為例耘斩,先定義一個(gè)showError方法沼填,在一開始需要將當(dāng)前狀態(tài)置為STATE_NETWORK_ERROR,接著inflate布局括授,注意坞笙,inflate之后需要進(jìn)行addView將加載失敗(網(wǎng)絡(luò)出錯(cuò))狀態(tài)的View添加到NetworkStateView中,這樣才可以進(jìn)行相應(yīng)的顯示隱藏操作

public void showError() {
    mCurrentState = STATE_NETWORK_ERROR;
    if (null == mErrorView) {
        mErrorView = mInflater.inflate(mErrorViewId, null);
        addView(mErrorView, 0, params);
    }
    showViewByState(mCurrentState);
}

showViewByState方法就是根據(jù)當(dāng)前的狀態(tài)來進(jìn)行相應(yīng)的View的切換

private void showViewByState(int state) {

    //如果當(dāng)前狀態(tài)為加載成功荚虚,隱藏此View薛夜,反之顯示
    this.setVisibility(state == STATE_SUCCESS ? View.GONE : View.VISIBLE);

    if (null != mLoadingView) {
        mLoadingView.setVisibility(state == STATE_LOADING ? View.VISIBLE : View.GONE);
    }

    if (null != mErrorView) {
        mErrorView.setVisibility(state == STATE_NETWORK_ERROR ? View.VISIBLE : View.GONE);
    }

    if (null != mNoNetworkView) {
        mNoNetworkView.setVisibility(state == STATE_NO_NETWORK ? View.VISIBLE : View.GONE);
    }

    if (null != mEmptyView) {
        mEmptyView.setVisibility(state == STATE_EMPTY ? View.VISIBLE : View.GONE);
    }
}

嗯...到這里其實(shí)也差不多了,不過還有一個(gè)問題版述,就是在加載失敗之后需要進(jìn)行刷新重新請(qǐng)求網(wǎng)絡(luò)怎么辦梯澜?哈哈,這當(dāng)然也是可以解決的渴析,我們只需要在定義一個(gè)刷新按鈕晚伙,并對(duì)外提供一個(gè)接口進(jìn)行調(diào)用就可以了吮龄,相應(yīng)的接口及方法為

public void setOnRefreshListener(OnRefreshListener listener) {
    mRefreshListener = listener;
}

public interface OnRefreshListener {
    void onRefresh();
}

那么showError可以改造如下

public void showError() {
    mCurrentState = STATE_NETWORK_ERROR;
    if (null == mErrorView) {
        mErrorView = mInflater.inflate(mErrorViewId, null);
        View errorRefreshView = mErrorView.findViewById(R.id.error_refresh_view);
        if (null != errorRefreshView) {
            errorRefreshView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    if (null != mRefreshListener) {
                        mRefreshListener.onRefresh();
                    }
                }
            });
        }
        addView(mErrorView, 0, params);
    }
    showViewByState(mCurrentState);
}

嗯,這樣就可以把多種狀態(tài)的View統(tǒng)一封裝在同一個(gè)View當(dāng)中咆疗,我們可以在Activity的布局文件中通過include標(biāo)簽加入NetworkStateView漓帚,接著我們只要調(diào)用NetworkStateView的相關(guān)方法就可以進(jìn)行多種狀態(tài)的切換了

詳細(xì)的代碼以及Sample可以去我的github查看,覺得還可以的不妨Star或follow

古語(yǔ)有云:不懶的程序員不是好程序員午磁,有人會(huì)覺得這樣也只是比第一種方法稍微簡(jiǎn)便一些尝抖,但也是需要在每個(gè)界面的布局文件中通過include標(biāo)簽進(jìn)行添加,并且進(jìn)行findViewById的操作然后才能調(diào)用相關(guān)方法迅皇,能不能進(jìn)行統(tǒng)一的設(shè)置呢昧辽?可以不需要在每個(gè)界面中進(jìn)行include,統(tǒng)一設(shè)置之后就可以調(diào)用相關(guān)方法登颓,這當(dāng)然也是可以的搅荞,我們可以在BaseActivity進(jìn)行相應(yīng)的設(shè)置,這樣子類Activity只需要調(diào)用BaseActivity的方法就可以挺据,至于怎么進(jìn)行設(shè)置取具,請(qǐng)等下篇講解。扁耐。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末产阱,一起剝皮案震驚了整個(gè)濱河市婉称,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌构蹬,老刑警劉巖王暗,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異庄敛,居然都是意外死亡俗壹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門藻烤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绷雏,“玉大人,你說我怎么就攤上這事怖亭∠严裕” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵兴猩,是天一觀的道長(zhǎng)期吓。 經(jīng)常有香客問我,道長(zhǎng)倾芝,這世上最難降的妖魔是什么讨勤? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任箭跳,我火速辦了婚禮,結(jié)果婚禮上潭千,老公的妹妹穿的比我還像新娘衅码。我一直安慰自己,他們只是感情好脊岳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布逝段。 她就那樣靜靜地躺著,像睡著了一般割捅。 火紅的嫁衣襯著肌膚如雪奶躯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天亿驾,我揣著相機(jī)與錄音嘹黔,去河邊找鬼。 笑死莫瞬,一個(gè)胖子當(dāng)著我的面吹牛儡蔓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疼邀,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼喂江,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了旁振?” 一聲冷哼從身側(cè)響起获询,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎拐袜,沒想到半個(gè)月后吉嚣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蹬铺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年尝哆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甜攀。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秋泄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赴邻,到底是詐尸還是另有隱情印衔,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布姥敛,位于F島的核電站奸焙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜与帆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一了赌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧玄糟,春花似錦勿她、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至郭卫,卻和暖如春砍聊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贰军。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工玻蝌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人词疼。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓俯树,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贰盗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子许饿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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