當(dāng)頁面加載數(shù)據(jù)失敗或者數(shù)據(jù)為空,我們應(yīng)該怎么辦?
體驗(yàn)良好的APP都會做相應(yīng)的處理。
比如網(wǎng)絡(luò)異常,會顯示一個網(wǎng)絡(luò)異常頁面,提示用戶去檢查網(wǎng)絡(luò);
數(shù)據(jù)為空時,出現(xiàn)一個溫馨的空頁面,引導(dǎo)用戶去創(chuàng)建數(shù)據(jù)等;
這些方法各大app都在使用,但是怎么發(fā)開?每一個頁面都寫一個空頁、面錯誤頁面和loading頁面嗎?那也太惡心了吧。
1、每個頁面都include 一個公用的空頁面/錯誤頁面,當(dāng)出現(xiàn)異常的時候動態(tài)的 visible/gone,想想都惡心,因?yàn)槊總€頁面都會出現(xiàn)這種情況,而且每個頁面還需要去處理相應(yīng)的邏輯,工作量非常大,不利于代碼的整潔
2、每當(dāng)出現(xiàn)異常的時候,動態(tài)的去向當(dāng)前的頁面 addview,這樣做會有很大的局限性,跟布局必須是相對布局,或者是幀布局,而且也要處理很多次相同的邏輯
經(jīng)過一段時間的苦想,想出了下面的解決方案,打造簡單靈活的支持Activity/Fragment 空頁面及錯誤頁面:
先簡單的看一下效果:
1、網(wǎng)絡(luò)異常情況:
這里我做的比較簡單,只是用廣播動態(tài)的去監(jiān)聽當(dāng)前的網(wǎng)絡(luò)狀況,當(dāng)沒有網(wǎng)絡(luò)的時候,通過 setcontentview 切換當(dāng)前 activity的頁面,當(dāng)有網(wǎng)絡(luò)的情況,再切換回去即可。
/**
* 注冊監(jiān)聽網(wǎng)絡(luò)的廣播
*/
private void registerReceiver() {
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
this.registerReceiver(myNetReceiver, filter);
}
/** * 處理廣播 */
private BroadcastReceiver myNetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) {
Log.e("dddd","網(wǎng)絡(luò)不可以用");
//顯示網(wǎng)絡(luò)異常頁面,具體方法下面贅述
showNetError(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}else {
Log.e("dddd","網(wǎng)絡(luò)可以用");
//隱藏網(wǎng)絡(luò)異常頁面,具體方法下面贅述
dimissNetError();
}
}
};
2、頁面空數(shù)據(jù)情況:
當(dāng)我們請求網(wǎng)絡(luò)數(shù)據(jù),數(shù)據(jù)為空的時候,顯示空面
/**
* 顯示空頁面
*/
showEmpty(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
}
});
/**
* 空頁面消失
*/
dimissEmpty();
看了上面的方法,是不是感覺還蠻像那么回事的,下面看看是怎么實(shí)現(xiàn)的(如果有什么不好的地方,盡管提出來)
1、先定義一個接口
public interface BaseView {
/**
* 展示加載框
*/
void showLoading();
/**
* 取消加載框展示
*/
void dismissLoading();
/**
* 顯示錯誤信息
*/
void showErrorMsg(String msg);
/**
* 顯示空頁面
*/
void showEmpty(SwipeRefreshLayout.OnRefreshListener listener);
/**
* 空頁面消失,恢復(fù)之前的頁面
*/
void dimissEmpty();
/**
* 顯示網(wǎng)絡(luò)錯誤頁面
*/
void showNetError(View.OnClickListener listener);
/**
* 網(wǎng)絡(luò)錯誤頁面,恢復(fù)之前的頁面
*/
void dimissNetError();
}
我的做法是,這些通用的東西,通通放到baseActivity 中
/**
* 基礎(chǔ) activity
* Created by Mr.Z on 16/7/12.
*/
public abstract class BaseActivity extends AppCompatActivity implements BaseView {
/**
* 緩沖框
*/
private MyBasicDialog myBasicDialog;
/**
* 下拉刷新
*/
protected SwipeRefreshLayout mSwipeRefreshLayout;
private LayoutInflater inflater;
/**
* 當(dāng)前頁面的布局
*/
protected View layoutMain = null;
/**
* 空頁面布局
*/
protected View layout_empty = null;
/**
* 網(wǎng)絡(luò)異常布局
*/
protected View layout_net_error = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
inflater = this.getLayoutInflater();
layoutMain = inflater.inflate(setLayoutId(), null);
layout_empty = inflater.inflate(R.layout.empty_activity, null);
layout_net_error = inflater.inflate(R.layout.net_error_activity, null);
setContentView(layoutMain);
initContentView(savedInstanceState);
registerReceiver();
}
/**
* 顯示緩沖框
*/
@Override
public void showLoading() {
WaitDialog waitDialog = new WaitDialog(this);
showDialog(waitDialog);
}
/**
* 設(shè)置當(dāng)前的布局id
*
* @return
*/
public abstract int setLayoutId();
/**
* 初始化UI
*
* @param savedInstanceState
*/
protected abstract void initContentView(Bundle savedInstanceState);
/**
* 緩沖框消失
*/
@Override
public void dismissLoading() {
hideDialog();
}
/**
* 錯誤提示
*/
@Override
public void showErrorMsg(String msg) {
Dialog alertDialog = new AlertDialog.Builder(this).
setTitle("溫馨提示").
setMessage(msg).
setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
}).
create();
alertDialog.show();
}
/**
* 顯示空頁面
*/
@Override
public void showEmpty(SwipeRefreshLayout.OnRefreshListener listener) {
setContentView(layout_empty);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.mSwipeRefreshLayout);
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light);
mSwipeRefreshLayout.setOnRefreshListener(listener);
}
/**
* 空頁面消失
*/
@Override
public void dimissEmpty() {
myHandler.sendEmptyMessage(3);
}
/**
* 網(wǎng)絡(luò)異常
*/
@Override
public void showNetError(View.OnClickListener listener) {
setContentView(layout_net_error);
LinearLayout root = (LinearLayout) findViewById(R.id.netlayout);
root.setOnClickListener(listener);
}
/**
* 隱藏網(wǎng)絡(luò)異常
*/
@Override
public void dimissNetError() {
setContentView(layoutMain);
}
/**
* 顯示對話框
*
* @param myBasicDialog
*/
private void showDialog(MyBasicDialog myBasicDialog) {
this.myBasicDialog = myBasicDialog;
myHandler.sendEmptyMessage(2);
}
/**
* 隱藏對話框
*/
private void hideDialog() {
myHandler.sendEmptyMessage(1);
}
private Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1://隱藏對話框
DialogManager.getInstance().hideDialog();
break;
case 2://顯示對話框
DialogManager.getInstance().showDialog(myBasicDialog);
break;
case 3://刷新頁面
mSwipeRefreshLayout.removeAllViews();
setContentView(layoutMain);
mSwipeRefreshLayout.setRefreshing(false);
break;
}
}
};
/**
* 切換 fragment
*
* @param contentId
* @param fragment
*/
public void switchFragment(int contentId, BaseFragment fragment) {
getSupportFragmentManager().beginTransaction().replace(contentId, fragment).addToBackStack(null).commit();
}
/**
* 切換空 fragment
*
* @param contentId
*/
public void switchEmptyFragment(int contentId, SwipeRefreshLayout.OnRefreshListener listener) {
EmptyFragment emptyFragment = new EmptyFragment();
emptyFragment.setOnRefresh(listener);
switchFragment(contentId, emptyFragment);
}
/**
* 隱藏空 fragment
*/
public void dissMissEmptyFragment() {
getSupportFragmentManager().popBackStack();
}
/**
* 切換網(wǎng)絡(luò)異常 fragment
*
* @param contentId
*/
public void switchNetErrorFragment(int contentId, View.OnClickListener listener) {
NetErrorFragment netErrorFragment = new NetErrorFragment();
netErrorFragment.setOnRefresh(listener);
switchFragment(contentId, netErrorFragment);
}
/**
* 隱藏網(wǎng)絡(luò)異常 fragment
*/
public void disMissNetErrorFragment() {
getSupportFragmentManager().popBackStack();
}
/**
* 點(diǎn)擊鍵盤以外部分,鍵盤消失
*
* @param ev
* @return
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (isShouldHideInput(v, ev)) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
return super.dispatchTouchEvent(ev);
}
// 必不可少,否則所有的組件都不會有TouchEvent了
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
/**
* 當(dāng)前鍵盤的顯示情況
*
* @param v
* @param event
* @return
*/
private boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] leftTop = {0, 0};
//獲取輸入框當(dāng)前的location位置
v.getLocationInWindow(leftTop);
int left = leftTop[0];
int top = leftTop[1];
int bottom = top + v.getHeight();
int right = left + v.getWidth();
if (event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom) {
// 點(diǎn)擊的是輸入框區(qū)域,保留點(diǎn)擊EditText的事件
return false;
} else {
return true;
}
}
return false;
}
/**
* 注冊監(jiān)聽網(wǎng)絡(luò)的廣播
*/
private void registerReceiver() {
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
this.registerReceiver(myNetReceiver, filter);
}
/**
* 處理廣播
*/
private BroadcastReceiver myNetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
ConnectivityManager connectivityManager=(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mobNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
NetworkInfo wifiNetInfo=connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) {
Log.e("dddd","網(wǎng)絡(luò)不可以用");
showNetError(new View.OnClickListener() {
@Override
public void onClick(View view) {
}
});
}else {
Log.e("dddd","網(wǎng)絡(luò)可以用");
dimissNetError();
}
}
};
}
ps:
1、空頁面中加入了SwipeRefreshLayout,實(shí)現(xiàn)下拉刷新頁面,并將接口暴露出來
2、網(wǎng)絡(luò)異常頁面,將點(diǎn)擊方法暴露出去
Github 地址.
如果覺得不錯,不要忘記在github上點(diǎn)??Star哦