NetworkStateView:界面多狀態加載

轉載 AlarmZ [http://www.lxweimin.com/p/858d41972d15]
他還有一個一個Android的基礎項目 [https://github.com/AlarmZeng/BaseProject]

在項目中經常需要進行不同狀態的加載,例如在網絡請求時的加載中狀態,加載失敗狀態,沒有網絡狀態和沒有數據的狀態等,之前在項目中的做法是把幾個不同的狀態布局都添加到需要進行狀態切換的Activity或Fragment的布局文件當中,接著再對每一個狀態界面進行相應的隱藏顯示,但是在界面一多的情況下,重復操作就會顯得很繁瑣。

在進行了無數次這樣繁瑣的操作后,有些受不了了,就想著能不能把這幾種狀態都封裝到同一個View中,在需要顯示不同的狀態時只需要調用相應的狀態方法就可以進行切換,這樣可比上一種方法簡便得多,哈哈,這當然是可以的,接下來就介紹一下NetworkStateView。

NetworkStateView繼承自LinearLayout,在里面定義了加載成功,加載中,加載出錯(這里只統一定義為網絡出錯,當然了用在哪種出錯方式上可以由你自己決定),沒有網絡,沒有數據五種狀態,其中加載成功表示用來顯示Activity或Fragment的界面,并用變量mCurrentState來記住當前顯示的狀態,相應的變量值如下:

//當前的加載狀態
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;

接著需要自定義屬性,用于傳入對應的狀態布局文件,在同一種狀態中如果需要有不同的界面顯示,便可以對應的傳入layout文件,這樣可以方便擴展

<declare-styleable name="NetworkStateView">

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

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

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

    <!-- 沒有網絡的布局id -->
    <attr name="noNetworkView" format="reference" />
    <!-- 沒有數據的布局圖片 -->
    <attr name="nsvNoNetworkImage" format="reference" />
    <!-- 沒有數據的布局文字 -->
    <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的構造函數中使用TypedArray進行相應屬性值的查找,注意,這里查找得到時布局文件的id,最后在顯示的時候需要對布局文件id進行相應的inflate,查找之后可以進行一些基本屬性的設置,例如LayoutParams和BackgroundColor等

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));
}

在使用屬性時,可以直接在NetworkStateView的布局文件中進行設置,又或者在styles文件中進行設置
直接在布局文件中聲明

 <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文件進行設置

<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>

進行上面的步驟后,就可以加載相應的布局文件了,以加載失敗(網絡出錯)為例,先定義一個showError方法,在一開始需要將當前狀態置為STATE_NETWORK_ERROR,接著inflate布局,注意,inflate之后需要進行addView將加載失敗(網絡出錯)狀態的View添加到NetworkStateView中,這樣才可以進行相應的顯示隱藏操作

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

showViewByState方法就是根據當前的狀態來進行相應的View的切換

private void showViewByState(int state) {

    //如果當前狀態為加載成功,隱藏此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);
    }
}

嗯...到這里其實也差不多了,不過還有一個問題,就是在加載失敗之后需要進行刷新重新請求網絡怎么辦?哈哈,這當然也是可以解決的,我們只需要在定義一個刷新按鈕,并對外提供一個接口進行調用就可以了,相應的接口及方法為

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);
}

嗯,這樣就可以把多種狀態的View統一封裝在同一個View當中,我們可以在Activity的布局文件中通過include標簽加入NetworkStateView,接著我們只要調用NetworkStateView的相關方法就可以進行多種狀態的切換了

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 在項目中經常需要進行不同狀態的加載,例如在網絡請求時的加載中狀態,加載失敗狀態,沒有網絡狀態和沒有數據的狀態等,之...
    HitoZeng閱讀 6,963評論 2 79
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,288評論 25 708
  • ¥開啟¥ 【iAPP實現進入界面執行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,523評論 0 17
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,241評論 4 61
  • 前段時間,因為李欣頻老師接觸到一個叫"行動派"的組織。他們很積極向上,我時刻都關注他們消息,跟隨著他們的指引往前走...
    宇辰_Tiffany閱讀 396評論 2 5