RecyclerView#Adapter支持无数据布局、错误布局和列表尾部的”没有更多了“布局
实际开发中,UI小姐姐都会提供通用的无数据页面
、错误提示页面
。
针对常见的支持下拉刷新和上拉加载更多的列表页面
,将他们的通用逻辑抽取出来,这样我们在开发过程中就只需要关注具体的业务逻辑了,无需每次通过cv来完善无数据页面
、错误提示页面
的逻辑了。
业务场景梳理
不支持分页
列表页根据业务场景中的是否需要分页
来进行区分,不支持分页的逻辑简单。具体如下图所示:
不分页的列表页,请求数据后就三种结果:有数据
、无数据
、错误(当前页面无数据的前提下)
。
当然了 不支持分页的页面也就不需要支持上拉加载更多
。
支持分页
支持分页的话情况稍微复杂一点,具体业务逻辑看下图:
请求失败的逻辑跟不分页的场景一致。
请求成功后,需要判断是否需要清除原有数据(第一页需要清除,表示下拉刷新过);需要根据当前页码(pageNo)
和每页数据容量(pageSize)
,判断是否还有更多数据需要加载
,如果没有更多数据了,则在列表底部显示没有更多
布局。
实现思路
通过上面的梳理,可以看出实现的核心在于让RecyclerView.Adapter默认支持空数据页面
、错误数据页面
、没有更多布局
。
这个需求我们可以通过RecyclerView.Adapter.getItemCount
和RecyclerView.Adapter.getItemViewType
来实现,通过唯一的type值
来进行区分。
另外,我们还需要支持扩展,我们自己也可以添加多种类型的item,需要预留入口。
demo地址
FuncRecyclerView
如何使用
1、添加布局FuncRecyclerView
这里我们给FuncRecyclerView设置的宽高都是固定的。
2、创建数据model、viewholder和自定义的CommonFuncItem
我们自己的Item对应的数据model,需要实现FuncBaseBean
接口,实现它的getViewType
方法,该方法的返回值大于0,且在当前FuncRecyclerView实例中唯一即可。具体代码如下:
public class TestFuncBean implements FuncBaseBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int getViewType() {
return 4;
}
}
接着创建ViewHolder,继承CommonFuncViewHolder,具体代码如下:
static class NormalViewHolder extends CommonFuncViewHolder {
public TextView tvName;
public NormalViewHolder(View itemView) {
super(itemView);
}
public NormalViewHolder(View itemView, FuncBaseBean funcBaseBean) {
super(itemView, funcBaseBean);
tvName = itemView.findViewById(R.id.tv);
}
}
使用创建好的Model和ViewHolder,创建自定义的CommonFuncItem,代码如下:
public class TestCommonFuncItem extends CommonFuncItem {
public TestCommonFuncItem(V v) {
super(v);
}
@Override
public CommonFuncViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false);
return new TestCommonFuncItem.NormalViewHolder(v, t);
}
@Override
public void onBindViewHolder(final CommonFuncViewHolder vh, FuncBaseBean funcBaseBean, final int position) {
final TestCommonFuncItem.NormalViewHolder holder = (NormalViewHolder) vh;
TestFuncBean rib = (TestFuncBean) funcBaseBean;
holder.tvName.setText(rib.getName());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(holder.itemView.getContext(), String.format("点击了第%d条数据", position), Toast.LENGTH_SHORT).show();
}
});
}
}
可以看出,onCreateViewHolder
和onBindViewHolder
方法跟RecyclerView.Adapter
中的使用方法一样,唯一的区别是,onBindViewHolder
方法中可以拿到对应的数据FuncBaseBean
,不需要我们根据position再去获取一次。
3、代码添加viewholder
接下来就是使用了,具体代码如下:
初始化添加type:
TestFuncBean testFuncBean = new TestFuncBean();
recyclerView.addCommonFuncItem(new TestCommonFuncItem(testFuncBean));
4、设置数据
网络请求成功和失败的情况:
public void onSuccess(List list) {
refreshLayout.finishLoadMore();
refreshLayout.finishRefresh();
PageListHelper.updateSinglePageList(recyclerView, list);
}
public void onFailure(Throwable e) {
refreshLayout.finishLoadMore();
refreshLayout.finishRefresh();
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
PageListHelper.showError(recyclerView,e);
}
/**
* 不分页的页面,更新数据
*
* @param recyclerView
* @param list
*/
public static void updateSinglePageList(FuncRecyclerView recyclerView, List list) {
if (list == null || list.isEmpty()) {
recyclerView.showEmpty();
} else {
recyclerView.setList(list);
}
}
public static void showError(FuncRecyclerView recyclerView, Throwable e) {
if (recyclerView.getListSize() == 0) {
recyclerView.showError();
}
}
不分页的demo请看SimplePageListActivity,支持分页的demo请看MultiPageListActivity