声明:
该结构是自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 extends K> newData) {
mData.addAll(position, newData);
notifyItemRangeInserted(position + 0, newData.size());
compatibilityDataSizeChanged(newData.size());
}
/**
* 将新数据添加到末尾
*
* @param newData 新数据集合
*/
public void addData(@NonNull Collection extends K> 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 extends K> 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下载