寫在前面
一般我們開發(fā)應(yīng)用的時候弃锐,比如首頁或者詳情界面,當(dāng)請求數(shù)據(jù)的時候挖炬,我們一般都會給用戶一個簡單的提示讯壶,比如加一個進(jìn)度條或者彈出一個Dialog料仗。但是有時候彈出Dialog的時候只是給用戶了一個提示,但是當(dāng)出現(xiàn)錯誤之后伏蚊,也只是簡單的一個Toast提示罢维,并不能再次進(jìn)行請求(有可能稍微做些處理,比如請求錯誤之后隱藏content布局然后在顯示一個錯誤布局)丙挽,第一種情況體驗很是糟糕,第二種情況稍微好點(diǎn)匀借,但是我們每次寫一個布局都需要多寫錯誤布局(或者寫一個公共的颜阐,然后在其他地方進(jìn)行引用,但是有一點(diǎn)就是我們在每次的activity或者fragment使用的時候都需要寫入邏輯)吓肋,造成不必要的代碼冗余凳怨。今天就來進(jìn)行封裝一下,完成一個侵入式的ProgressFragment是鬼。
開始實現(xiàn)
第一步分析
我們首先要實現(xiàn)的封裝肤舞,能夠達(dá)到以下的幾個要求
- 1、加載的時候顯示Loading狀態(tài)
- 2均蜜、數(shù)據(jù)為空或者錯誤的時候顯示數(shù)據(jù)為錯誤或者其他提示
- 3李剖、正常情況顯示正常數(shù)據(jù)狀態(tài)
- 4、當(dāng)“數(shù)據(jù)為空或者錯誤狀態(tài)”的時候囤耳,可以點(diǎn)擊進(jìn)行重新請求數(shù)據(jù)
原理分析
看到那四個需要實現(xiàn)的條件篙顺,我們大致可以了解到,需要有三種布局狀態(tài)正常布局狀態(tài)充择、錯誤布局狀態(tài)德玫、Loading狀態(tài)有了這幾種狀態(tài),我們就可以在請求的時候根據(jù)我們定義的以下方法(或者類似方法),然后控制不同布局的顯示和加載
- showLoading()方法中顯示Loading狀態(tài)布局
- showError()或者showNoData()方法顯示錯誤狀態(tài)布局
- 其余正常狀態(tài)顯示正常布局狀態(tài)(也就是剛開始這個布局是顯示visiable)
代碼實現(xiàn)
有了分析椎麦,我們首先可以知道需要三種布局(錯誤宰僧、加載、正常)
錯誤布局
<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>
布局完成之后观挎,首先說明一下琴儿,他們?nèi)齻€的頂層布局是FrameLayout(ViewGroup的子類)
然后看下邏輯代碼怎么實現(xiàn)的吧段化。
第一步就是找到布局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);
//對錯誤布局實現(xiàn)點(diǎn)擊事件 完成加載出錯或者數(shù)據(jù)為空的時候 點(diǎn)擊重新請求數(shù)據(jù)邏輯
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);
//加載出錯或者數(shù)據(jù)為空顯示的錯誤布局的一個提示
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實現(xiàn)對控件的注解
init();
//用于子類實現(xiàn) 請求數(shù)據(jù)方法
initData();
}
setRealContentView()方法
/**
* 設(shè)置真正的布局
*/
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);
}
然后在寫剩余的兩種邏輯凤类,這樣寫沒有錯穗泵,但是感覺代碼是有點(diǎn)冗余而且也不太好看,前面我們提到過谜疤,跟布局是一個FrameLayout佃延,屬于ViewGroup的子類,那么他就有getChildCount()方法獲取子view那么我們就可以這樣寫邏輯了夷磕。
/**
* 判斷要顯示的子view 正常布局 錯誤布局 loading布局 根據(jù)傳入的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); //隱藏
}
}
}
然后接下來這樣寫三個邏輯
/**
* 顯示進(jìn)度條view
*/
public void showProgressView() {
showView(R.id.view_progress);
}
/**
* 顯示真實布局view
*/
public void showContentView() {
showView(R.id.view_content);
}
/**
* 顯示數(shù)據(jù)為空view
*/
public void showEmptyView() {
showView(R.id.view_empty);
}
/**
* 顯示數(shù)據(jù)為空view
*/
public void showEmptyView(int resId) {
showView(R.id.view_empty);
mEmptyTextView.setVisibility(View.VISIBLE);
mEmptyTextView.setText(resId);
}
/**
* 顯示數(shù)據(jù)為空view
*/
public void showEmptyView(String msgId) {
showView(R.id.view_empty);
mEmptyTextView.setVisibility(View.VISIBLE);
mEmptyTextView.setText(msgId);
}
第四步子類使用
這個時候基本邏輯就差不多完事了履肃,然后剩余的就需要子Fragment或者子activity進(jìn)行繼承和重寫方法,實現(xiàn)邏輯了坐桩。比如
重新請求數(shù)據(jù)
@Override
public void onEmptyViewClick() {
super.onEmptyViewClick();
//重新請求數(shù)據(jù)
mPresenter.requestDatas();
}
顯示錯誤和數(shù)據(jù)為空邏輯
@Override
public void showNoData() {
//showError("沒有數(shù)據(jù)進(jìn)行展示");
showEmptyView("沒有數(shù)據(jù)進(jìn)行展示");
Toast.makeText(getActivity(), "沒有數(shù)據(jù)進(jìn)行展示", Toast.LENGTH_SHORT).show();
}
@Override
public void showError(String msg) {
// showError(msg);
showEmptyView(msg);
Toast.makeText(getActivity(), "服務(wù)器開小差了" + msg, Toast.LENGTH_SHORT).show();
}
這里整體邏輯就算實現(xiàn)了尺棋,完整的代碼我就不貼了,這里直接給出git地址