RecyclerView#Adapter支持無數據佈局、錯誤佈局和列表尾部的”沒有更多了“佈局
實際開發中,UI小姐姐都會提供通用的無數據頁面
、錯誤提示頁面
。
針對常見的支持下拉刷新和上拉加載更多的列表頁面
,將他們的通用邏輯抽取出來,這樣我們在開發過程中就只需要關注具體的業務邏輯了,無需每次通過cv來完善無數據頁面
、錯誤提示頁面
的邏輯了。
業務場景梳理
不支持分頁
列表頁根據業務場景中的是否需要分頁
來進行區分,不支持分頁的邏輯簡單。具體如下圖所示:
不分頁的列表頁,請求數據後就三種結果:有數據
、無數據
、錯誤(當前頁面無數據的前提下)
。
當然了 不支持分頁的頁面也就不需要支持上拉加載更多
。
支持分頁
支持分頁的話情況稍微複雜一點,具體業務邏輯看下圖:
請求失敗的邏輯跟不分頁的場景一致。
請求成功後,需要判斷是否需要清除原有數據(第一頁需要清除,表示下拉刷新過);需要根據當前頁碼(pageNo)
和每頁數據容量(pageSize)
,判斷是否還有更多數據需要加載
,如果沒有更多數據了,則在列表底部顯示沒有更多
佈局。
實現思路
通過上面的梳理,可以看出實現的核心在於讓RecyclerView.Adapter默認支持空數據頁面
、錯誤數據頁面
、沒有更多佈局
。
這個需求我們可以通過RecyclerView.Adapter.getItemCount
和RecyclerView.Adapter.getItemViewType
來實現,通過唯一的type值
來進行區分。
另外,我們還需要支持擴展,我們自己也可以添加多種類型的item,需要預留入口。
demo地址
如何使用
1、添加布局FuncRecyclerView
這裏我們給FuncRecyclerView設置的寬高都是固定的。
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.tinytongtong.funcrecyclerview.recyclerview.FuncRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
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<V extends FuncBaseBean> extends CommonFuncItem<V> {
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<FuncBaseBean> 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<FuncBaseBean> 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