Android(安卓)基于DataBinding的通用RecyclerView Adapter
声明:
该结构是自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下载
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 一句话锁定MySQL数据占用元凶
- Android(安卓)解决ListView里面多套布局多个EditText数据混乱问
- 1.Android的四大组件
- Android之Adapter用法总结(转载)
- Android数据库SQLite增改查删操作演示
- 27款Python 测试工具开源软件
- Android数据绑定组件RoboBinding使用详解
- android SharedPreferences 记录数据