声明:
该结构是自BRVAH框架修改而来,有兴趣的朋友可以优先了解下BRVAH适配器框架。
支持:
几乎支持原生RecyclerView.Adapter所能实现的所有效果。
使用:
/**
* 单类型适配器
* 注意点:
* layoutItem对应布局需要设置名为 item的variable
*
* @param recyclerView
* @param layoutItem
* @param 数据实体类型
*/
private void singleAdapter(RecyclerView recyclerView, @LayoutRes int layoutItem) {
BaseHxAdapter adapter = new BaseHxAdapter<>(layoutItem);
//将适配器与RecyclerView绑定
adapter.bindToRecyclerView(recyclerView);
//设置加载更多监听,该步骤同时也做了bindToRecyclerView的操作,如果设置加载更多监听,那么可以不用调用adapter.bindToRecyclerView(recyclerView);
adapter.setOnLoadMoreListener(() -> {
}, recyclerView);
//该方法需要在bindToRecyclerView()之后调用,或者在adapter.setOnLoadMoreListener之后调用,确保RecyclerView对象已与Adapter绑定
//判断数据是否满屏,如果没有满屏,那么将会隐藏加载更多,否则显示
adapter.disableLoadMoreIfNotFullPage();
//表示是否启用加载更多;可用adapter.disableLoadMoreIfNotFullPage()代替,但是disableLoadMoreIfNotFullPage方法在数据未满屏时,默认不启用加载更多
adapter.setEnableLoadMore(true);
//表示一页数据加载完成
adapter.loadMoreComplete();
//表示全部数据加载结束,参数 true or false;true时,表示不显示“没有更多数据了”,false表示显示
adapter.loadMoreEnd(true);
//表示加载更多时数据加载失败
adapter.loadMoreFail();
//设置新数据集,一般在请求完数据或者刷新数据后调用
adapter.setNewData(null);
//表示在当前数据集末尾,新增一个数据集,一般在加载更多请求完成后调用
adapter.addData((ArrayList) null);
//获取数据实体对象
adapter.getItem(0);
//获取适配器的数据集
List list = adapter.getData();
//移除某行数据
adapter.remove(0);
//替换数据集内容;当前适配器的数据集对象引用不变,只变数据;如果想改变适配器的数据集的引用,可以调用setNewData();
adapter.replaceData((ArrayList) null);
}
/**
* 多类型数据适配器;
* 注意点:
* 1.数据实体需实现IMultiItemType接口,并与layoutItems对应
* 2.layoutItems对应布局需要设置名为 item的variable
*
* @param recyclerView
* @param layoutItems
*/
private void multipleAdapter(RecyclerView recyclerView, @LayoutRes int... layoutItems) {
BaseMultiItemHxAdapter adapter = new BaseMultiItemHxAdapter() {
@Override
protected void bindTypeAndLayout() {
for (int i = 0; i < layoutItems.length; i++) {
addItemType(i, layoutItems[0]);
}
}
};
//其余步骤,与singleAdapter相同
}
关键代码:
/** * function:RecyclerView适配器 * author: frj * modify date: 2018/6/7 */ public class BaseHxAdapter
extends RecyclerView.Adapter { /** * 加载更多类型 */ private static final int TYPE_LOAD_MORE = 0x111; /** * 普通项类型 */ private static final int TYPE_ITEM = 0; /** * 加载图层 */ private LoadMoreView mLoadMoreView; //表示加载状态是否可用 private boolean mNextLoadEnable = false; /** * 表示是否启用加载更多 */ private boolean mLoadMoreEnable = false; /** * 表示加载更多是否是正在加载中 */ private boolean mLoading = false; private int mLastPosition = -1; private RecyclerView mRecyclerView; /** * 加载更多监听 */ private OnLoadMoreListener onLoadMoreListener; /** * 数据条目布局id */ private @LayoutRes int mItemLayoutId; /** * 数据集合 */ protected List mData; protected Context mContext; protected LayoutInflater mLayoutInflater; public BaseHxAdapter(@LayoutRes int layoutResId) { mData = new ArrayList<>(); this.mItemLayoutId = layoutResId; this.mLoadMoreView = new SimpleLoadMoreView(); } public BaseHxAdapter(@LayoutRes int layoutResId, List data) { this(layoutResId); setNewData(data); } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { RecyclerView.ViewHolder viewHolder = null; this.mContext = parent.getContext(); this.mLayoutInflater = LayoutInflater.from(mContext); //加载更多 if (viewType == TYPE_LOAD_MORE) { viewHolder = new LoadMoreViewHolder(getItemView(getLoadMoreView().getLayoutId(), parent)); } else { viewHolder = V.create(parent, getItemLayoutId(viewType)); } return viewHolder; } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { int itemType = getItemViewType(position); //由于LoadMore是一个单独的ViewHolder类型,所以此处单独加判断 if (itemType == TYPE_LOAD_MORE) { if (mLoadMoreView != null) { autoLoadMore(position); mLoadMoreView.convert((LoadMoreViewHolder) holder); } } else { bindDataItem(holder, position); } } @Override public int getItemCount() { return mData.size() + getLoadMoreViewCount(); } @Override public int getItemViewType(int position) { if (mLoadMoreEnable) { if (position == mData.size()) { return TYPE_LOAD_MORE; } } return getDefItemViewType(position); } /** * View附加到Window回调 * * @param holder */ @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { super.onViewAttachedToWindow(holder); int type = holder.getItemViewType(); if (TYPE_LOAD_MORE == type) { setFullSpan(holder); } } /** * 获取Item布局 * * @return */ protected int getItemLayoutId(int viewType) { return mItemLayoutId; } /** * 加载不同的项类型 * * @param position * @return */ protected int getDefItemViewType(int position) { return TYPE_ITEM; } /** * 绑定数据对象 * * @param holder * @param position */ protected void bindDataItem(RecyclerView.ViewHolder holder, int position) { if (holder != null) { ItemViewHolder itemViewHolder = (ItemViewHolder) holder; itemViewHolder.bindItem(getItem(position)); } } /** * 设置新数据 * * @param data */ public void setNewData(@Nullable List data) { this.mData = data == null ? new ArrayList () : data; if (onLoadMoreListener != null) { mNextLoadEnable = true; mLoadMoreEnable = true; mLoading = false; mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT); } mLastPosition = -1; notifyDataSetChanged(); } /** * 在指定位置插入数据 * * @param position * @param item * @deprecated 用 {@link #addData(int, Object)} 插入 */ @Deprecated public void add(@IntRange(from = 0) int position, @NonNull K item) { addData(position, item); } /** * 插入新数据至指定位置 * * @param position */ public void addData(@IntRange(from = 0) int position, @NonNull K data) { mData.add(position, data); notifyItemInserted(position); compatibilityDataSizeChanged(1); } /** * add one new data */ public void addData(@NonNull K data) { mData.add(data); notifyItemInserted(mData.size()); compatibilityDataSizeChanged(1); } /** * 移除指定位置项 * * @param position */ public void remove(@IntRange(from = 0) int position) { if (mData == null || position < 0 || position >= mData.size()) { return; } mData.remove(position); int internalPosition = position; notifyItemRemoved(internalPosition); compatibilityDataSizeChanged(0); notifyItemRangeChanged(internalPosition, mData.size() - internalPosition); } /** * 改变数据 */ public void setData(@IntRange(from = 0) int index, @NonNull K data) { mData.set(index, data); notifyItemChanged(index); } /** * 添加新数据至指定位置 * * @param position 插入位置 * @param newData 新数据集 */ public void addData(@IntRange(from = 0) int position, @NonNull Collection newData) { mData.addAll(position, newData); notifyItemRangeInserted(position + 0, newData.size()); compatibilityDataSizeChanged(newData.size()); } /** * 将新数据添加到末尾 * * @param newData 新数据集合 */ public void addData(@NonNull Collection newData) { mData.addAll(newData); notifyItemRangeInserted(mData.size() - newData.size(), newData.size()); compatibilityDataSizeChanged(newData.size()); } /** * use data to replace all item in mData. this method is different {@link #setNewData(List)}, * it doesn't change the mData reference * * @param data data collection */ public void replaceData(@NonNull Collection data) { // 不是同一个引用才清空列表 if (data != mData) { mData.clear(); mData.addAll(data); } notifyDataSetChanged(); } /** * 兼容的数据改变 * * @param size 需要兼容的数据大小 */ private void compatibilityDataSizeChanged(int size) { final int dataSize = mData == null ? 0 : mData.size(); if (dataSize == size) { notifyDataSetChanged(); } } /** * 获取数据项集合 * * @return 列表数据 */ @NonNull public List getData() { return mData; } /** * 获取指定位置的数据项 * * @param position * @return */ @Nullable public K getItem(@IntRange(from = 0) int position) { if (position >= 0 && position < mData.size()) { return mData.get(position); } else { return null; } } /** * 刷新项数据 * * @param position */ public final void refreshNotifyItemChanged(int position) { notifyItemChanged(position); } /** * 检查RecyclerView对象是否为空 */ private void checkNotNull() { if (mRecyclerView == null) { throw new RuntimeException("please bind recyclerView first!"); } } protected RecyclerView getRecyclerView() { return mRecyclerView; } private void setRecyclerView(RecyclerView recyclerView) { mRecyclerView = recyclerView; } /** * 绑定RecyclerView对象,如果已绑定,抛出异常 */ public void bindToRecyclerView(RecyclerView recyclerView) { if (getRecyclerView() != null) { throw new RuntimeException("Don't bind twice"); } setRecyclerView(recyclerView); if (getRecyclerView().getAdapter() == null) { getRecyclerView().setAdapter(this); } } /** * 获取加载更多管理类 * * @return */ protected LoadMoreView getLoadMoreView() { if (mLoadMoreView == null) { mLoadMoreView = new SimpleLoadMoreView(); } return mLoadMoreView; } /** * 绑定自定义的加载更多 * * @param loadMoreView */ public void setLoadMoreView(LoadMoreView loadMoreView) { if (loadMoreView == null) { return; } this.mLoadMoreView = loadMoreView; } /** * 打开加载更多 * * @param onLoadMoreListener */ private void openLoadMore(OnLoadMoreListener onLoadMoreListener) { this.onLoadMoreListener = onLoadMoreListener; mNextLoadEnable = true; mLoadMoreEnable = true; mLoading = false; } /** * 设置加载更多监听 * * @param onLoadMoreListener * @param recyclerView */ public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener, RecyclerView recyclerView) { openLoadMore(onLoadMoreListener); bindToRecyclerView(recyclerView); } /** * 返回加载更多的启用状态 * * @return true表示启用,false表示未启用 */ public boolean isLoadMoreEnable() { return mLoadMoreEnable; } /** * 加载更多的数量 * * @return 0 or 1 */ public int getLoadMoreViewCount() { if (onLoadMoreListener == null || !mLoadMoreEnable) { return 0; } if (!mNextLoadEnable && mLoadMoreView.isLoadEndMoreGone()) { return 0; } if (mData.size() == 0) { return 0; } return 1; } /** * Gets to load more locations * * @return */ public int getLoadMoreViewPosition() { return mData.size(); } /** * 设置加载更多的启用状态 * * @param enable true表示启用加载更多;false表示不启用 */ public void setEnableLoadMore(boolean enable) { int oldLoadMoreCount = getLoadMoreViewCount(); mLoadMoreEnable = enable; int newLoadMoreCount = getLoadMoreViewCount(); if (oldLoadMoreCount == 1) { if (newLoadMoreCount == 0) { notifyItemRemoved(getLoadMoreViewPosition()); } } else { if (newLoadMoreCount == 1) { mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT); notifyItemInserted(getLoadMoreViewPosition()); } } } /** * @return 适配器是否正在显示加载更多进度 */ public boolean isLoading() { return mLoading; } /** * 加载结束,没有更多数据 */ public void loadMoreEnd() { loadMoreEnd(false); } /** * 加载结束没有更多数据 * * @param gone true表示隐藏加载更多图层 */ public void loadMoreEnd(boolean gone) { if (getLoadMoreViewCount() == 0) { return; } mLoading = false; mNextLoadEnable = false; mLoadMoreView.setLoadMoreEndGone(gone); if (gone) { notifyItemRemoved(getLoadMoreViewPosition()); } else { mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END); notifyItemChanged(getLoadMoreViewPosition()); } } /** * 加载完成 */ public void loadMoreComplete() { if (getLoadMoreViewCount() == 0) { return; } mLoading = false; mNextLoadEnable = true; mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT); notifyItemChanged(getLoadMoreViewPosition()); } /** * 加载失败 */ public void loadMoreFail() { if (getLoadMoreViewCount() == 0) { return; } mLoading = false; mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL); notifyItemChanged(getLoadMoreViewPosition()); } /** * 通知开始回调并加载更多 */ public void notifyLoadMoreToLoading() { if (mLoadMoreView.getLoadMoreStatus() == LoadMoreView.STATUS_LOADING) { return; } mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT); notifyItemChanged(getItemCount() - 1); } /** * 如果数据没有全屏,那么禁用加载更多; * 使用前线绑定RecyclerView * * @see #disableLoadMoreIfNotFullPage(RecyclerView) */ public void disableLoadMoreIfNotFullPage() { checkNotNull(); disableLoadMoreIfNotFullPage(getRecyclerView()); } /** * * 不是配置项!! *
* 这个方法是用来检查是否满一屏的,所以只推荐在 {@link #setNewData(List)} 之后使用 * 原理很简单,先关闭 load more,检查完了再决定是否开启 *
* 不是配置项!! * * @param recyclerView your recyclerView * @see #setNewData(List) */ private void disableLoadMoreIfNotFullPage(RecyclerView recyclerView) { setEnableLoadMore(false); if (recyclerView == null) { return; } RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager == null) { return; } if (manager instanceof LinearLayoutManager) { final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager; recyclerView.postDelayed(new Runnable() { @Override public void run() { if (isFullScreen(linearLayoutManager)) { setEnableLoadMore(true); } } }, 50); } else if (manager instanceof StaggeredGridLayoutManager) { final StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) manager; recyclerView.postDelayed(new Runnable() { @Override public void run() { final int[] positions = new int[staggeredGridLayoutManager.getSpanCount()]; staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions); //得到当前显示的最大位置数 int pos = getTheBiggestNumber(positions) + 1; if (pos != getItemCount()) { setEnableLoadMore(true); } } }, 50); } } /** * 解析布局文件,获取View * * @param layoutResId 布局文件id * @param parent * @return */ protected View getItemView(@LayoutRes int layoutResId, ViewGroup parent) { return mLayoutInflater.inflate(layoutResId, parent, false); } /** * 得到最大数字 * * @param numbers * @return */ private int getTheBiggestNumber(int[] numbers) { int tmp = -1; if (numbers == null || numbers.length == 0) { return tmp; } for (int num : numbers) { if (num > tmp) { tmp = num; } } return tmp; } /** * 设置holder对应类型布局铺满 * * @param holder 要铺满的Holde */ protected void setFullSpan(RecyclerView.ViewHolder holder) { if (holder.itemView.getLayoutParams() instanceof StaggeredGridLayoutManager.LayoutParams) { StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder .itemView.getLayoutParams(); params.setFullSpan(true); } } /** * 判断是否满屏 * * @param llm * @return */ private boolean isFullScreen(LinearLayoutManager llm) { return (llm.findLastCompletelyVisibleItemPosition() + 1) != getItemCount() || llm.findFirstCompletelyVisibleItemPosition() != 0; } /** * 自动加载更多 * * @param position */ private void autoLoadMore(int position) { if (TYPE_LOAD_MORE != getItemViewType(position)) { return; } if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) { return; } mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING); if (!mLoading) { mLoading = true; if (mRecyclerView != null) { mRecyclerView.post(new Runnable() { @Override public void run() { if (onLoadMoreListener != null) { onLoadMoreListener.onLoadMore(); } } }); } else { if (onLoadMoreListener != null) { onLoadMoreListener.onLoadMore(); } } } } /** * 加载更多监听 */ public interface OnLoadMoreListener { /** * 加载更多回掉 */ void onLoadMore(); } }
/**
* function:RecyclerView多类型适配器
* author: frj
* modify date: 2018/6/7
*/
public abstract class BaseMultiItemHxAdapter extends BaseHxAdapter {
/**
* 布局引用集合
*/
private SparseIntArray layouts;
/**
* 默认的布局类型
*/
private static final int DEFAULT_VIEW_TYPE = -0xff;
/**
* 类型未找到
*/
public static final int TYPE_NOT_FOUND = -404;
public BaseMultiItemHxAdapter() {
this(null);
}
public BaseMultiItemHxAdapter(List data) {
super(0, data);
bindTypeAndLayout();
}
@Override
protected int getDefItemViewType(int position) {
T item = mData.get(position);
if (item != null) {
return item.getItemType();
}
return DEFAULT_VIEW_TYPE;
}
@Override
protected void bindDataItem(RecyclerView.ViewHolder holder, int position) {
if (holder != null) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.bindItem(getItem(position));
}
}
/**
* 绑定类型和布局
*/
protected abstract void bindTypeAndLayout();
/**
* 根据类型获取布局id
*
* @param viewType
* @return
*/
private int getLayoutId(int viewType) {
return layouts.get(viewType, TYPE_NOT_FOUND);
}
/**
* 添加项类型及对应的布局文件id
*
* @param type 项类型
* @param layoutResId 布局文件id
*/
protected void addItemType(int type, @LayoutRes int layoutResId) {
if (layouts == null) {
layouts = new SparseIntArray();
}
layouts.put(type, layoutResId);
}
/**
* 根据类型返回布局id
*
* @param viewType
* @return
*/
@Override
protected int getItemLayoutId(int viewType) {
return getLayoutId(viewType);
}
}
public class ItemViewHolder extends RecyclerView.ViewHolder {
public final T binding;
/**
* 创建ItemViewHolder
*
* @param parent
* @param layoutRes
* @return
*/
public static ItemViewHolder create(@NonNull ViewGroup parent, @LayoutRes int layoutRes) {
return new ItemViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), layoutRes, parent, false));
}
public ItemViewHolder(T binding) {
super(binding.getRoot());
this.binding = binding;
}
/**
* 绑定数据对象
*
* @param item
* @param
*/
public void bindItem(K item) {
binding.setVariable(BR.item, item);
binding.executePendingBindings();
}
}
/**
* function:
* author: frj
* modify date: 2018/6/11
*/
public interface IMultiItemType {
/**
* 获取项类型
*
* @return
*/
int getItemType();
}
说明:
由于在ItemViewHolder中的bindItem()方法中,绑定的是固定名称 BR.item,如果项目中没有名为item的variable,项目编译会不通过,解决办法是在布局文件中定义名为item的variable。每个Item的布局文件建议都包含名为item的variable.
Demo下载