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 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下载


你可能感兴趣的:(Android)