###寫在前面 一般我們開發應用的時候,比如首頁或者詳情介面,當請求資料的時候,我們一般都會給使用者一個簡單的提示,比如加一個進度條或者彈出一個Dialog。但是有時候彈出Dialog的時候只是給使用者了一個提示,但是當出現錯誤之後,也只是簡單的一個Toast提示,並不能再次進行請求(有可能稍微做些處理,比如請求錯誤之後隱藏content佈局然後在顯示一個錯誤佈局),第一種情況體驗很是糟糕,第二種情況稍微好點,但是我們每次寫一個佈局都需要多寫錯誤佈局(或者寫一個公共的,然後在其他地方進行引用,但是有一點就是我們在每次的activity或者fragment使用的時候都需要寫入邏輯),造成不必要的程式碼冗餘。今天就來進行封裝一下,完成一個侵入式的ProgressFragment。 ###開始實現 ####第一步分析 我們首先要實現的封裝,能夠達到以下的幾個要求
-
1、載入的時候顯示Loading狀態
-
2、資料為空或者錯誤的時候顯示資料為錯誤或者其他提示
-
3、正常情況顯示正常資料狀態
-
4、當“資料為空或者錯誤狀態”的時候,可以點選進行重新請求資料 ####原理分析 看到那四個需要實現的條件,我們大致可以瞭解到,需要有三種佈局狀態***正常佈局狀態***、錯誤佈局狀態、***Loading狀態***有了這幾種狀態,我們就可以在請求的時候根據我們定義的以下方法(或者類似方法),然後控制不同佈局的顯示和載入
-
showLoading()方法中顯示***Loading狀態佈局***
-
showError()或者showNoData()方法顯示***錯誤狀態佈局***
-
其餘正常狀態顯示***正常佈局狀態***(也就是剛開始這個佈局是顯示visiable)
####程式碼實現 有了分析,我們首先可以知道需要三種佈局(錯誤、載入、正常)
錯誤佈局
<LinearLayout
android:id="@+id/view_empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:visibility="gone">
<TextView
android:id="@+id/text_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/empty_data"/>
</LinearLayout>
複製程式碼
載入佈局
<LinearLayout
android:id="@+id/view_progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/text_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/loading"/>
</LinearLayout>
複製程式碼
正常佈局
<FrameLayout
android:id="@+id/view_content"
android:layout_width="match_parent"
android:layout_height="match_parent">
</FrameLayout>
複製程式碼
佈局完成之後,首先說明一下,他們三個的頂層佈局是FrameLayout(ViewGroup的子類) 然後看下邏輯程式碼怎麼實現的吧。
#####第一步就是找到佈局id
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//跟佈局
mRootView = (FrameLayout) inflater.inflate(R.layout.fragment_progress, container, false);
//空佈局也就是錯誤佈局
mViewEmpty = mRootView.findViewById(R.id.view_empty);
//對錯誤佈局實現點選事件 完成載入出錯或者資料為空的時候 點選重新請求資料邏輯
mViewEmpty.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onEmptyViewClick();
}
});
//載入loading佈局
mViewProgress = mRootView.findViewById(R.id.view_progress);
//真實的佈局
mViewContent = (FrameLayout) mRootView.findViewById(R.id.view_content);
//載入出錯或者資料為空顯示的錯誤佈局的一個提示
mEmptyTextView = (TextView) mRootView.findViewById(R.id.text_tip);
return mRootView;
}
複製程式碼
#####第二步確定什麼時候載入真實佈局
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.mApplication = (CNMarketApplication) getActivity().getApplication();
setupAcitivtyComponent(mApplication.getAppComponent());
//載入真實的佈局
setRealContentView();
//用於初始化佈局控制元件 這裡也可以不寫 因為我們使用了ButterKnife實現對控制元件的註解
init();
//用於子類實現 請求資料方法
initData();
}
複製程式碼
setRealContentView()方法
/**
* 設定真正的佈局
*/
private void setRealContentView() {
View realContentView = LayoutInflater.from(getActivity()).inflate(setLayout(), mViewContent, true);
mUnbinder = ButterKnife.bind(this, realContentView);
}
複製程式碼
#####第三步控制三種佈局的顯示 我們這個時候要寫控制三種佈局的顯示和隱藏的邏輯,這個時候可能有人會這樣寫
/**
* 顯示載入loading佈局
*/
public void showProgressView(){
mViewProgress.setVisibility(View.VISIBLE);
mViewEmpty.setVisibility(View.GONE);
mViewContent.setVisibility(View.GONE);
}
複製程式碼
然後在寫剩餘的兩種邏輯,這樣寫沒有錯,但是感覺程式碼是有點冗餘而且也不太好看,前面我們提到過,跟佈局是一個FrameLayout,屬於ViewGroup的子類,那麼他就有*getChildCount()*方法獲取子view那麼我們就可以這樣寫邏輯了。
/**
* 判斷要顯示的子view 正常佈局 錯誤佈局 loading佈局 根據傳入的viewId
*
* @param viewId 需要顯示的viewid
*/
public void showView(int viewId) {
int childCount = mRootView.getChildCount();
for (int i = 0; i < childCount; i++) {
if (mRootView.getChildAt(i).getId() == viewId) {
mRootView.getChildAt(i).setVisibility(View.VISIBLE);
} else {
mRootView.getChildAt(i).setVisibility(View.GONE); //隱藏
}
}
}
複製程式碼
然後接下來這樣寫三個邏輯
/**
* 顯示進度條view
*/
public void showProgressView() {
showView(R.id.view_progress);
}
/**
* 顯示真實佈局view
*/
public void showContentView() {
showView(R.id.view_content);
}
/**
* 顯示資料為空view
*/
public void showEmptyView() {
showView(R.id.view_empty);
}
/**
* 顯示資料為空view
*/
public void showEmptyView(int resId) {
showView(R.id.view_empty);
mEmptyTextView.setVisibility(View.VISIBLE);
mEmptyTextView.setText(resId);
}
/**
* 顯示資料為空view
*/
public void showEmptyView(String msgId) {
showView(R.id.view_empty);
mEmptyTextView.setVisibility(View.VISIBLE);
mEmptyTextView.setText(msgId);
}
複製程式碼
####第四步子類使用 這個時候基本邏輯就差不多完事了,然後剩餘的就需要子Fragment或者子activity進行繼承和重寫方法,實現邏輯了。比如
重新請求資料
@Override
public void onEmptyViewClick() {
super.onEmptyViewClick();
//重新請求資料
mPresenter.requestDatas();
}
複製程式碼
顯示錯誤和資料為空邏輯
@Override
public void showNoData() {
//showError("沒有資料進行展示");
showEmptyView("沒有資料進行展示");
Toast.makeText(getActivity(), "沒有資料進行展示", Toast.LENGTH_SHORT).show();
}
@Override
public void showError(String msg) {
// showError(msg);
showEmptyView(msg);
Toast.makeText(getActivity(), "伺服器開小差了" + msg, Toast.LENGTH_SHORT).show();
}
複製程式碼
這裡整體邏輯就算實現了,完整的程式碼我就不貼了,這裡直接給出git地址