在使用paging的过程中,使用起来是很方便,但也发现一些问题,比如往下滑的时候,没有加载的提示(数据正在加载中),这个时候就需要我们去处理了,数据加载可能是成功了,可能是失败了,也可能是全部加载完成了,加载过程中还有加载的结果都是需要一个友好的提示的,我们看到好多APP应用都是在item底部会有一个友好的提示,所有这边文章就是来实现如何在底部添加一个友好提示的item,数据加载成功后会自动消失。
加载数据的几种状态
加载数据可以分为加载过程中,加载成功,加载失败,数据全部加载完成,如果还有其他状态可自行添加,根据这些状态就可以定义一个枚举类来描述这几种情况:
public enum RequestDataState {
//加载过程中
LOADING,
//加载成功
SUCCESS,
//加载失败
FAIL,
//全部加载完成
COMPLETE
}
数据加载
对于paging来说,数据加载的功能全部交给了DataSource,DataSource有三个子类,我们可以根据不同的需求去实现对应的子类,这里以PageKeyedDataSource为例进行说明,其他的类似,为了处理加载数据的几种状态并且通用性强,可以将这个功能抽象出来,之后要同步加载状态的全部去实现这个类就可以了,这个还比较简单,类的定义如下:
public abstract class PageKeyedLoadDataSource<K, V> extends PageKeyedDataSource<K, V> {
//存储请求数据后的状态
private MutableLiveData<RequestDataState> netDataStateLiveData = new MutableLiveData<>();
public MutableLiveData<RequestDataState> getNetDataStateLiveData() {
return netDataStateLiveData;
}
@Override
public void loadAfter(@NonNull LoadParams<K> params, @NonNull LoadCallback<K, V> callback) {
updateNetState(RequestDataState.LOADING);
RequestDataState requestDataState = loadAfter1(params, callback);
//加载数据失败后,为下次重试加载提供参数
if (requestDataState == RequestDataState.FAIL) {
this.params = params;
this.callback = callback;
}
updateNetState(requestDataState);
}
private LoadParams<K> params;
private LoadCallback<K, V> callback;
//数据加载失败后可以尝试重新进行加载
public void retryAfter(){
loadAfter(params,callback);
}
private void updateNetState(RequestDataState state) {
netDataStateLiveData.postValue(state);
}
/**
* @param params
* @param callback
* @return true加载数据成功 false加载数据失败
*/
public abstract RequestDataState loadAfter1(@NonNull LoadParams<K> params, @NonNull LoadCallback<K,V> callback);
}
这里提供了一个抽象方法loadAfter1(),返回请求数据的状态就好,这样封装好后,其他使用都是一样的,所以这里还是比较方便的,接下来才是重点。
数据加载状态显示
对于数据显示,自然想到的就是PagedListAdapter了,这里就是通过它来显示加载状态的,问题又来了,如何才能解耦呢?就是再次封装下PagedListAdapter,处理对请求数据状态的显示,如下:
public abstract class PagedListLoadAdapter<T, VH extends RecyclerView.ViewHolder> extends PagedListAdapter<T, RecyclerView.ViewHolder> {
//定义加载数据显示的几种状态
private static final int TYPE_LOAD_LOADING = 1002;
private static final int TYPE_LOAD_ALL_COMPLETE = 1003;
private static final int TYPE_LOAD_FAIL = 1004;
//当前请求数据的状态
private RequestDataState state;
private LifecycleOwner owner;
PagedListLoadAdapter(@NonNull DiffUtil.ItemCallback diffCallback, LifecycleOwner owner) {
super(diffCallback);
this.owner = owner;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int type) {
//这里加载过程中、加载失败、加载全部完成使用的是同一个布局,可根据需要自行定义
if (type == TYPE_LOAD_LOADING || type == TYPE_LOAD_ALL_COMPLETE || type == TYPE_LOAD_FAIL) {
View inflate = View.inflate(viewGroup.getContext(), R.layout.item_load_more, null);
return new LoadingViewHolder(inflate);
}
return onCreateViewHolder1(viewGroup, type);
}
public abstract VH onCreateViewHolder1(@NonNull ViewGroup viewGroup, int type);
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
//根据加载状态对item的提示的处理
if (getItemViewType(position) == TYPE_LOAD_LOADING) {
((LoadingViewHolder) viewHolder).bindTo("正在加载数据,请稍候...");
return;
}
if (getItemViewType(position) == TYPE_LOAD_ALL_COMPLETE) {
((LoadingViewHolder) viewHolder).bindTo("所有数据已全部加载完成");
return;
}
if (getItemViewType(position) == TYPE_LOAD_FAIL) {
((LoadingViewHolder) viewHolder).bindTo("加载数据失败");
return;
}
onBindViewHolder1(viewHolder, position);
}
public abstract void onBindViewHolder1(@NonNull RecyclerView.ViewHolder viewHolder, int position);
@Override
public int getItemCount() {
if (state == RequestDataState.LOADING || state == RequestDataState.COMPLETE || state == RequestDataState.FAIL) {
return super.getItemCount() + 1;
}
return super.getItemCount();
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1 && (state == RequestDataState.LOADING))
return TYPE_LOAD_LOADING;
if (position == getItemCount() - 1 && (state == RequestDataState.FAIL))
return TYPE_LOAD_FAIL;
if (position == getItemCount() - 1 && (state == RequestDataState.COMPLETE)) {
View view = new TextView((Activity) owner);
//3秒后不显示加载完成的提示
view.postDelayed(() -> {
PagedListLoadAdapter.this.state = RequestDataState.SUCCESS;
notifyItemChanged(getItemCount());
}, 3000);
return TYPE_LOAD_ALL_COMPLETE;
}
return super.getItemViewType(position);
}
@Override
public void onCurrentListChanged(@Nullable PagedList<T> currentList) {
super.onCurrentListChanged(currentList);
PagedList<T> pagedList = getCurrentList();
if (null == pagedList) {
return;
}
DataSource<?, T> dataSource = pagedList.getDataSource();
if (dataSource instanceof PageKeyedLoadDataSource) {
MutableLiveData<RequestDataState> netDataStateLiveData = ((PageKeyedLoadDataSource) dataSource).getNetDataStateLiveData();
//对加载数据状态进行监听
netDataStateLiveData.observe(owner, this::setNetState);
}
}
public void setNetState(RequestDataState state) {
this.state = state;
notifyItemChanged(getItemCount());
}
private class LoadingViewHolder extends RecyclerView.ViewHolder {
private TextView loadView;
public LoadingViewHolder(@NonNull View itemView) {
super(itemView);
loadView = itemView.findViewById(R.id.load_msg);
}
public void bindTo(String s) {
loadView.setText(s);
}
}
}
到这就算是完成了,其他所有的操作所有的和正常使用paging是一样的,只是继承的类换成是上面的PageKeyedLoadDataSource和PagedListLoadAdapter,这里需要注意PagedListLoadAdapter中定义的加载布局需要我们去定义,这样做完后,网络加载数据的提示就会自动完成。如果想要实现下拉刷新,可以使用android原生的SwipeRefreshLayout,同时需要替换掉PagedList,也就是重新在创建一个LivePagedListBuilder就ok了。