背景:
一般我们开发android app的时候,最经常遇到的一个情况就是处理各种各样的页面加载状态,如:加载中,加载失败,数据为空。
而一般我们的做法有写个BaseActivity,在这里面放处理各种状态,如果有遇到Fragment,则又需要写个BaseFragment,之后,我们又要根据加载的结果,去显示相应的状态。
如果是列表数据,则又会写出一个BaseListActivity之类的,如果有新的需求,则又会冒出新的Base类。
当然,本人并不是说这么写不行。只是这么写重复代码之多,扩展性也不好,对于一个严格遵守KISS准则的人而言,苦不堪言。
解决
既然上面有这种问题,那我们应该怎么处理呢,首先要明确的是,一个app,他的加载状态,一般而言只有一套,特殊页面可能需要特殊处理。针对这点,我想到的是,写一个ILoadStateHelper接口,单纯用以控制页面加载状态:
public interface ILoadStateHelper {
/**
* 显示内容视图
*/
void showContent();
/**
* 显示加载中视图
*/
void showLoading();
/**
* 显示空界面视图
*/
void showEmpty();
/**
* 显示错误视图
*/
void showError(boolean isEmpty,Throwable t);
/**
* 重新加载监听
*
* @param listener
*/
void setReloadListener(OnReloadListener listener);
}
之后呢,单单这点还不够,一般页面也有刷新功能,为了处理刷新,写一个IRefreshHelper接口:
public interface IRefreshHelper {
/**
* 刷新完成
*
* @param isSuccess 是否加载成功
*/
void refreshComplete(boolean isSuccess);
/**
* 设置刷新监听
*
* @param listener 刷新监听
*/
void setOnRefreshListener(OnRefreshListener listener);
/**
* 是否在刷新中
*
* @return true:刷新,false:不刷新
*/
boolean isRefreshing();
}
再然后,就是处理列表数据了,这里要做的事情就比较多了,其实主要就是控制自动加载下一页,写一个IListViewHelper:
public interface IListViewHelper {
/**
* 刷新adapter
*/
void notifyAdapter();
/**
* 显示空闲状态
*/
void showLoadMoreIdle();
/**
* 显示加载中状态
*/
void showLoadMoreLoading();
/**
* 显示无更多状态
*/
void showLoadMoreNoMore();
/**
* 显示错误状态
*/
void showLoadMoreError();
/**
* 设置加载更多监听
*
* @param listener 加载更多监听
*/
void setOnLoadMoreListener(OnLoadMoreListener listener);
/**
* 设置重新加载更多监听
*
* @param listener 重新加载更多监听
*/
void setOnReLoadMoreListener(OnReloadListener listener);
/**
* 是否加载更多
*
* @return 是否加载更多
*/
boolean isLoadingMore();
}
这几个helper,可以处理常见的页面加载逻辑,一般而言,一个app,我们只要定好BaseLoadStateHelper,BaseRefreshHelper,BaseListViewHelper,就可以在每个页面中使用了。
然后下面才是重头戏,配合我标题中所说的Loader,加载的相关逻辑全部用loader去处理就好了,可以使得页面又清爽,又干净。
那么什么是Loader呢,Loader是用来加载数据的一个工具类,它需要配合Model来使用,Model是Activity对应页面的数据模型,Loader通过Model去加载数据,并根据Model的数据是否为空,以及根据上面几个helper,显示相应的界面。
这里的代码量相对就比较多,故就不贴出来了。
使用:
Loader的使用非常的简便。
首先你需要将你的BaseActivity这么去写
public class BaseActivity extends AppCompatActivity implements INetworkView {
private DataLoader mLoader;
@Override
protected void onDestroy() {
super.onDestroy();
destroyLoader();
}
@Override
public void destroyLoader() {
if (mLoader != null) {
mLoader.onDestroy();
}
}
@Override
public void setDataLoader(DataLoader loader) {
mLoader = loader;
}
}
这么做是为了在destroy的时候,取消加载,释放内存。
其次,定义好上面的几个helper,
之后,写好Model:
public class SingleDataModel extends Model<String> {
private String response;
@Override
public boolean isEmpty() {
return TextUtils.isEmpty(response);
}
@Override
public void setData(boolean isRefreshing, String response) {
this.response = response;
}
@Override
protected Call<String> getModelCall() {
MainApi api = ApiManager.getInstance().getMainApi();
return api.getTestCall();
}
}
或者:
public class ListDataModel extends ListModel<String, String> {
public ListDataModel(List<String> list) {
super(list);
}
@Override
protected boolean ensureHasNext(String response, List<String> mapList) {
return page < 10;
}
@Override
protected List<String> map(String response) {
List<String> list = new ArrayList<>();
int size = mList.size();
for (int i = 0; i < 20; i++) {
list.add((i + size) + ":XXXXXXXXXXXXXXXXXXXXXXXXXX");
}
return list;
}
@Override
protected Call<String> getModelCall() {
MainApi api = ApiManager.getInstance().getMainApi();
return api.getTestCall();
}
}
之后就是在Activity中调用了:
public class SingleDataActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_single_data);
SwipeRefreshLayout layout = (SwipeRefreshLayout) findViewById(R.id.act_single_data_sfl);
final TextView textView = (TextView) findViewById(R.id.act_single_data_tv);
SingleDataModel model = new SingleDataModel();
DataLoader<String> loader = new DataLoader<>(this, model);
loader.setLoadStateHelper(new LoadStateHelper(layout));
loader.setRefreshViewHelper(new RefreshHelper(layout));
loader.setOnLoadSuccessListener(new OnLoadSuccessListener<String>() {
@Override
public void onSuccess(boolean isRefreshing, String response) {
textView.setText(response);
}
});
loader.load();
}
}
或者
public class ListDataActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_list_data);
SwipeRefreshLayout layout = (SwipeRefreshLayout) findViewById(R.id.act_list_data_sfl);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.act_list_data_rv);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
List<String> list = new ArrayList<>();
MyAdapter adapter = new MyAdapter(list);
recyclerView.setAdapter(adapter);
ListDataModel model = new ListDataModel(list);
ListLoader<String, String> loader = new ListLoader<>(this, model);
loader.setLoadStateHelper(new LoadStateHelper(layout));
loader.setRefreshViewHelper(new RefreshHelper(layout));
loader.setListViewHelper(new RecyclerViewHelper(recyclerView));
loader.load();
}
}
备注:
Loader是用Retrofit2+Rxjava2。
Loader也可以有缓存作用,本人才疏学浅,不知道如何同http的缓存,去做到在有网络比较慢的情况下,也能先显示缓存的内容,在去服务器端获取,或者判断缓存是否过期再去获取数据。故只能自己写一个拙劣的缓存控制策略,如有好的建议,还希望不吝赐教。
如需实现缓存,则需要在Model中,重写getCacheStrategy方法,如:
public class ListDataModel extends ListModel<String, String> {
public ListDataModel(List<String> list) {
super(list);
}
@Override
protected boolean ensureHasNext(String response, List<String> mapList) {
return page < 10;
}
@Override
protected List<String> map(String response) {
List<String> list = new ArrayList<>();
int size = mList.size();
for (int i = 0; i < 20; i++) {
list.add((i + size) + ":XXXXXXXXXXXXXXXXXXXXXXXXXX");
}
return list;
}
@Override
protected Call<String> getModelCall() {
MainApi api = ApiManager.getInstance().getMainApi();
return api.getTestCall();
}
@Override
protected CacheStrategy<String> getCacheStrategy(Request request) {
return new CacheStrategy<String>(request, CacheStrategy.CacheType.READ_CACHE_UPDATE_UI_THEN_NET, 180) {
@Override
public boolean isTimeOut() {
return false;
}
@Override
public String readCache() {
return null;
}
@Override
public void saveCache(String s) {
}
};
}
}
至此,Loader的介绍基本结束,没怎么写博文,写的不好,还望大家谅解。
传送门:
compile 'com.github.linxin6560:loader:1.0.7'