BaseQuickAdapter

作者: 桑享 | 来源:发表于2017-04-10 15:13 被阅读9932次

    用法

    普通布局BaseQuickAdapter

    BaseQuickAdapter homeAdapter = new BaseQuickAdapter<HomeItem,BaseViewHolder>(R.layout.home_item_view, mDataList){
    
                @Override
                protected void convert(BaseViewHolder helper, HomeItem item) {
                    helper.setText(R.id.text, item.getTitle());
                    helper.setImageResource(R.id.icon, item.getImageResource());
                }
            };
    

    多布局BaseMultiItemQuickAdapter

    数据源类型需要继承自MultiItemEntity,MultiItemEntity是一个接口

    原理

    BaseQuickAdapter实例化第一步当然是调用我们的构造方法:

     public BaseQuickAdapter(int layoutResId, List<T> data) {
            this.mData = data == null ? new ArrayList<T>() : data;
            if (layoutResId != 0) {
                this.mLayoutResId = layoutResId;
            }
        }
     public BaseQuickAdapter(List<T> data) {
            this(0, data);
        }
    
     public BaseQuickAdapter(int layoutResId) {
            this(layoutResId, null);
        }
    

    getItemViewType

    @Override
        public int getItemViewType(int position) {
            if (getEmptyViewCount() == 1) {
                boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
                switch (position) {
                    case 0:
                        if (header) {
                            return HEADER_VIEW;
                        } else {
                            return EMPTY_VIEW;
                        }
                    case 1:
                        if (header) {
                            return EMPTY_VIEW;
                        } else {
                            return FOOTER_VIEW;
                        }
                    case 2:
                        return FOOTER_VIEW;
                    default:
                        return EMPTY_VIEW;
                }
            }
            //当RecyclerView在渲染一个新的itemView时,就会判断是不是需要调用加载更多回调,需要就调用
            autoLoadMore(position);
            int numHeaders = getHeaderLayoutCount();
            if (position < numHeaders) {
                return HEADER_VIEW;
            } else {
                int adjPosition = position - numHeaders;
                int adapterCount = mData.size();
                if (adjPosition < adapterCount) {
                    return getDefItemViewType(adjPosition);
                } else {
                    adjPosition = adjPosition - adapterCount;
                    int numFooters = getFooterLayoutCount();
                    if (adjPosition < numFooters) {
                        return FOOTER_VIEW;
                    } else {
                        return LOADING_VIEW;
                    }
                }
            }
        }
    

    根据我们data的index值以及我们是否开启空视图之类的数据来决定在onCreateViewHolder中应该返回什么类型的viewHolder。

    onCreateViewHolder

    @Override
        public K onCreateViewHolder(ViewGroup parent, int viewType) {
            K baseViewHolder = null;
            this.mContext = parent.getContext();
            this.mLayoutInflater = LayoutInflater.from(mContext);
            switch (viewType) {
                case LOADING_VIEW:
                    baseViewHolder = getLoadingView(parent);
                    break;
                case HEADER_VIEW:
                    baseViewHolder = createBaseViewHolder(mHeaderLayout);
                    break;
                case EMPTY_VIEW:
                    baseViewHolder = createBaseViewHolder(mEmptyLayout);
                    break;
                case FOOTER_VIEW:
                    baseViewHolder = createBaseViewHolder(mFooterLayout);
                    break;
                default:
                //创建BaseViewHolder或者其拓展的类
                    baseViewHolder = onCreateDefViewHolder(parent, viewType);
                    bindViewClickListener(baseViewHolder);
            }
            baseViewHolder.setAdapter(this);
            return baseViewHolder;
    
        }
    
     protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
            return createBaseViewHolder(getItemView(layoutResId, parent));
        }
    
    /**
         * if you want to use subclass of BaseViewHolder in the adapter,
         * you must override the method to create new ViewHolder.
         *
         * @param view view
         * @return new ViewHolder
         */
        protected K createBaseViewHolder(View view) {
            Class temp = getClass();
            Class z = null;
            while (z == null && null != temp) {
                z = getInstancedGenericKClass(temp);
                temp = temp.getSuperclass();
            }
            K k = createGenericKInstance(z, view);
            return null != k ? k : (K) new BaseViewHolder(view);
        }
    
    /**
         * try to create Generic K instance
         *
         * @param z
         * @param view
         * @return
         */
        private K createGenericKInstance(Class z, View view) {
            try {
                Constructor constructor;
                String buffer = Modifier.toString(z.getModifiers());
                String className = z.getName();
                // inner and unstatic class
                if (className.contains("$") && !buffer.contains("static")) {
                    constructor = z.getDeclaredConstructor(getClass(), View.class);
                    return (K) constructor.newInstance(this, view);
                } else {
                    constructor = z.getDeclaredConstructor(View.class);
                    return (K) constructor.newInstance(view);
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
            return null;
        }
    
    

    getLayoutPosition
    注意这里使用了ViewHolder的getLayoutPosition方法,此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。

    注:获取的位置和用户目前在屏幕上看到的是一致的.
    onBindViewHolder

     /**
         * To bind different types of holder and solve different the bind events
         *
         * @param holder
         * @param positions
         * @see #getDefItemViewType(int)
         */
        @Override
        public void onBindViewHolder(K holder, int positions) {
            int viewType = holder.getItemViewType();
    
            switch (viewType) {
                case 0:
    
                    convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                    break;
                case LOADING_VIEW:
                    mLoadMoreView.convert(holder);
                    break;
                case HEADER_VIEW:
                    break;
                case EMPTY_VIEW:
                    break;
                case FOOTER_VIEW:
                    break;
                default:
                    convert(holder, mData.get(holder.getLayoutPosition() - getHeaderLayoutCount()));
                    break;
            }
        }
    

    添加Header和Footer

    默认的recycleview是没有addheaderView和addfooterview的,需要自定义

    添加header(footer原理一样)

    //添加Header对应的View
    View view = getLayoutInflater().inflate(R.layout.head_view, (ViewGroup) mRecyclerView.getParent(), false);
    //添加Header对应的点击事件
    view.setOnClickListener(listener);
    //调用BaseQuickAdapter
    headerAndFooterAdapter.addHeaderView(headerView);
    
    //(0,header.child)属于正常,其他情况下默认添加到head末尾
    public int addHeaderView(View header) {
            return addHeaderView(header, -1);
        }
    public int addHeaderView(View header, int index) {
            return addHeaderView(header, index, LinearLayout.VERTICAL);
        }
    public int addHeaderView(View header, int index, int orientation) {
            //如果没有header的话就新增一个HeaderLayout
            if (mHeaderLayout == null) {
                mHeaderLayout = new LinearLayout(header.getContext());
                if (orientation == LinearLayout.VERTICAL) {
                    mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
                    mHeaderLayout.setLayoutParams(new LayoutParams(MATCH_PARENT, WRAP_CONTENT));
                } else {
                    mHeaderLayout.setOrientation(LinearLayout.HORIZONTAL);
                    mHeaderLayout.setLayoutParams(new LayoutParams(WRAP_CONTENT, MATCH_PARENT));
                }
            }
            //获取mHeaderLayout中的header个数
            final int childCount = mHeaderLayout.getChildCount();
            //如果index的小于0的话,表示插入在最前面index置为view的个数
            //如果index大于childcount,index置为子view的个数
            //校验index数据的正确性(0,index)往中间插入
            if (index < 0 || index > childCount) {
                index = childCount;
            }
            //将数据插入到headerlayout中
            mHeaderLayout.addView(header, index);
            //如果有一个header的话
            if (mHeaderLayout.getChildCount() == 1) {
                //获取新增的header的位置position为0
                int position = getHeaderViewPosition();
                if (position != -1) {
                    //通知插入一个数据位置0处
                    notifyItemInserted(position);
                }
            }
            return index;
        }
    

    加载更多

    使用

    pullToRefreshAdapter.setOnLoadMoreListener(this, mRecyclerView);
     pullToRefreshAdapter.setLoadMoreView(new CustomLoadMoreView());
    pullToRefreshAdapter.setEnableLoadMore(true);
     
    

    然后在网络请求,

    请求成功的话调用loadMoreComplete()

    将数据加载到RecycleView中去

    请求失败的话调用loadMoreFail()

    @Override
        public void onLoadMoreRequested() {
    
                    //加载更多的请求
                    mSwipeRefreshLayout.setEnabled(false);
                    if (pullToRefreshAdapter.getData().size() < PAGE_SIZE) {
                        pullToRefreshAdapter.loadMoreEnd(true);
                    } else {
                        if (mCurrentCounter >= TOTAL_COUNTER) {
                            pullToRefreshAdapter.loadMoreEnd(mLoadMoreEndGone);//true is gone,false is visible
                        } else {
                            new Handler().postDelayed(new Runnable() {
                                @Override
                                public void run() {
                                    pullToRefreshAdapter.addData(DataServer.getSampleData(PAGE_SIZE));
                                    mCurrentCounter = pullToRefreshAdapter.getData().size();
                                    pullToRefreshAdapter.loadMoreComplete();
                                    mSwipeRefreshLayout.setEnabled(true);
                                }
                            },1000);
                        }
                    }
        }
    

    原理分析

    添加loadermoreview项

     /**
         * Set the enabled state of load more.
         *
         * @param enable True if load more is enabled, false otherwise.
         */
        public void setEnableLoadMore(boolean enable) {
            int oldLoadMoreCount = getLoadMoreViewCount();
            mLoadMoreEnable = enable;
            int newLoadMoreCount = getLoadMoreViewCount();
            //oldLoadMoreCount 代表在改变这个开关时我们是否处于显示上拉加载的view的状态,1表示处于该状态。
            //newLoadMoreCount 代表我们当前是否可以开启上拉加载功能,同样,1表示可以。
            if (oldLoadMoreCount == 1) {
                //加入当前处于显示加载更多view的状态,移除加载更多view。
                if (newLoadMoreCount == 0) {
                    notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
                }
            } else {
                //开启上拉加载
                if (newLoadMoreCount == 1) {
                    mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_DEFAULT);
                    notifyItemInserted(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
                }
            }
        }
    

    当绘制view的时候当绘制到底部的时候会添加回调
    显示LoadingView

    private void autoLoadMore(int position) {
            //只有开启了上拉加载且loadMoreView没有gone且data.size>0 ,且设置了mRequestLoadMoreListener时返回1
            if (getLoadMoreViewCount() == 0) {
                return;
            }
            /*
            理解起来大概是这样的,mAutoLoadMoreSize是标识开启自动加载更多的一个数量阀值。这个返回很巧妙。
            假设你的data.size =20 ,mAutoLoadMoreSize =10,当前position=9, 按照理解,这个pisition=9是个临界值,因为我们设置了剩余数量<10个时自动加载更多,此时计算9<20-10,position等于9,说明后面还有10个数据没渲染,当position=10时(未加载数据还剩9个,此时应该预加载更多),10<20-10,不成立,代码继续往下走,
            执行
            */
            if (position < getItemCount() - mAutoLoadMoreSize) {
                return;
            }
            //当你快速上滑时,由于position>=10后满足条件,执行加载更多的回调,position=11时也会执行,以此类推,那么你将收到多次加载更多的回调。所以我们需要判断此时是否当前的加载更能多回调已完成,保证每次到达阀值后只调用一次加载更多回调方法。
            if (mLoadMoreView.getLoadMoreStatus() != LoadMoreView.STATUS_DEFAULT) {
                return;
            }
            mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_LOADING);
            if (!mLoading) {
                mLoading = true;
                if (getRecyclerView() != null) {
                    getRecyclerView().post(new Runnable() {
                        @Override
                        public void run() {
                            mRequestLoadMoreListener.onLoadMoreRequested();
                        }
                    });
                } else {
                    mRequestLoadMoreListener.onLoadMoreRequested();
                }
            }
        }
    
    

    加载失败调用,可能你有需求在加载失败后要显示一个加载失败的view提示用户,而不是直接关闭loadMoreView。此时你可以调用该方法。

     /**
         * Refresh failed
         */
        public void loadMoreFail() {
            if (getLoadMoreViewCount() == 0) {
                return;
            }
            mLoading = false;
            mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_FAIL);
            notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
        }
    
     /**
         * Refresh end, no more data
         *
         * @param gone if true gone the load more view
         */
        public void loadMoreEnd(boolean gone) {
            if (getLoadMoreViewCount() == 0) {
                return;
            }
            mLoading = false;
            mNextLoadEnable = false;
            mLoadMoreView.setLoadMoreEndGone(gone);
            if (gone) {
                notifyItemRemoved(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
            } else {
                mLoadMoreView.setLoadMoreStatus(LoadMoreView.STATUS_END);
                notifyItemChanged(getHeaderLayoutCount() + mData.size() + getFooterLayoutCount());
            }
        }
    

    EmptyView设置

    调用setEmptyView()

    源码

    public void setEmptyView(View emptyView) {
            boolean insert = false;
            if (mEmptyLayout == null) {
                mEmptyLayout = new FrameLayout(emptyView.getContext());
                final LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
                final ViewGroup.LayoutParams lp = emptyView.getLayoutParams();
                if (lp != null) {
                    layoutParams.width = lp.width;
                    layoutParams.height = lp.height;
                }
                mEmptyLayout.setLayoutParams(layoutParams);
                insert = true;
            }
            mEmptyLayout.removeAllViews();
            mEmptyLayout.addView(emptyView);
            mIsUseEmpty = true;
            if (insert) {
                if (getEmptyViewCount() == 1) {
                    int position = 0;
                    if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
                        position++;
                    }
                    notifyItemInserted(position);
                }
            }
        }
    

    mEmptyLayout赋值
    应该只有空数据(EmptyView,Header,Footer等等)
    会执行getItemViewType来显示EmptyView

    if (getEmptyViewCount() == 1) {
                boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
                switch (position) {
                    case 0:
                        if (header) {
                            return HEADER_VIEW;
                        } else {
                            return EMPTY_VIEW;
                        }
                    case 1:
                        if (header) {
                            return EMPTY_VIEW;
                        } else {
                            return FOOTER_VIEW;
                        }
                    case 2:
                        return FOOTER_VIEW;
                    default:
                        return EMPTY_VIEW;
                }
            }
    

    当有数据的时候
    getEmptyViewCount()就为0,不会显示Empty数据

    BaseMultiItemQuickAdapter多布局实现

    使用
    实体必须继承自MultiItemEntity
    然后添加类型
    addItemType(MultipleItem.TEXT, R.layout.item_text_view);
    addItemType(MultipleItem.IMG, R.layout.item_image_view);
    addItemType(MultipleItem.IMG_TEXT, R.layout.item_img_text_view);

    在getItemViewType中会

     protected int getDefItemViewType(int position) {
            if (mMultiTypeDelegate != null) {
                return mMultiTypeDelegate.getDefItemViewType(mData, position);
            }
            return super.getItemViewType(position);
        }
    

    返回类型type

    在onCreateViewHolder中会调用

    protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
            int layoutId = mLayoutResId;
            if (mMultiTypeDelegate != null) {
                layoutId = mMultiTypeDelegate.getLayoutId(viewType);
            }
            return createBaseViewHolder(parent, layoutId);
        }
    

    根据viewtype获取到不同的layout
    使用的是SparseArray<Integer>存储的layout

    
     protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
            return createBaseViewHolder(getItemView(layoutResId, parent));
        }
    

    点击事件

    SimpleClickListener实现
    注:点击事件的实现有多重方式,如:

    1,使用 RecyclerView提供的 addOnItemTouchListener()实现

    2,创建 ItemView时添加点击事件监听

    3,在 ItemView attach RecyclerView时实现

    根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,而所有的adapter的刷新并不总是及时的,只有这个方法返回的才是当前item经过一些变换后所处的真正位置。
    使用

    adapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
                @Override
                public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
                    Log.d(TAG, "onItemClick: ");
                    Toast.makeText(ItemClickActivity.this, "onItemClick" + position, Toast.LENGTH_SHORT).show();
                }
            });
    

    源码

    /**
         * Interface definition for a callback to be invoked when an itemchild in this
         * view has been clicked
         */
        public interface OnItemChildClickListener {
            /**
             * callback method to be invoked when an item in this view has been
             * click and held
             *
             * @param view     The view whihin the ItemView that was clicked
             * @param position The position of the view int the adapter
             */
            void onItemChildClick(BaseQuickAdapter adapter, View view, int position);
        }
    
    
        /**
         * Interface definition for a callback to be invoked when an childView in this
         * view has been clicked and held.
         */
        public interface OnItemChildLongClickListener {
            /**
             * callback method to be invoked when an item in this view has been
             * click and held
             *
             * @param view     The childView whihin the itemView that was clicked and held.
             * @param position The position of the view int the adapter
             * @return true if the callback consumed the long click ,false otherwise
             */
            boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position);
        }
    
        /**
         * Interface definition for a callback to be invoked when an item in this
         * view has been clicked and held.
         */
        public interface OnItemLongClickListener {
            /**
             * callback method to be invoked when an item in this view has been
             * click and held
             *
             * @param adapter  the adpater
             * @param view     The view whihin the RecyclerView that was clicked and held.
             * @param position The position of the view int the adapter
             * @return true if the callback consumed the long click ,false otherwise
             */
            boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position);
        }
    
    
        /**
         * Interface definition for a callback to be invoked when an item in this
         * RecyclerView itemView has been clicked.
         */
        public interface OnItemClickListener {
    
            /**
             * Callback method to be invoked when an item in this RecyclerView has
             * been clicked.
             *
             * @param adapter  the adpater
             * @param view     The itemView within the RecyclerView that was clicked (this
             *                 will be a view provided by the adapter)
             * @param position The position of the view in the adapter.
             */
            void onItemClick(BaseQuickAdapter adapter, View view, int position);
        }
    
        /**
         * Register a callback to be invoked when an item in this RecyclerView has
         * been clicked.
         *
         * @param listener The callback that will be invoked.
         */
        public void setOnItemClickListener(@Nullable OnItemClickListener listener) {
            mOnItemClickListener = listener;
        }
    
        /**
         * Register a callback to be invoked when an itemchild in View has
         * been  clicked
         *
         * @param listener The callback that will run
         */
        public void setOnItemChildClickListener(OnItemChildClickListener listener) {
            mOnItemChildClickListener = listener;
        }
    
        /**
         * Register a callback to be invoked when an item in this RecyclerView has
         * been long clicked and held
         *
         * @param listener The callback that will run
         */
        public void setOnItemLongClickListener(OnItemLongClickListener listener) {
            mOnItemLongClickListener = listener;
        }
    
        /**
         * Register a callback to be invoked when an itemchild  in this View has
         * been long clicked and held
         *
         * @param listener The callback that will run
         */
        public void setOnItemChildLongClickListener(OnItemChildLongClickListener listener) {
            mOnItemChildLongClickListener = listener;
        }
    
    
        /**
         * @return The callback to be invoked with an item in this RecyclerView has
         * been long clicked and held, or null id no callback as been set.
         */
        public final OnItemLongClickListener getOnItemLongClickListener() {
            return mOnItemLongClickListener;
        }
    
        /**
         * @return The callback to be invoked with an item in this RecyclerView has
         * been clicked and held, or null id no callback as been set.
         */
        public final OnItemClickListener getOnItemClickListener() {
            return mOnItemClickListener;
        }
    
        /**
         * @return The callback to be invoked with an itemchild in this RecyclerView has
         * been clicked, or null id no callback has been set.
         */
        @Nullable
        public final OnItemChildClickListener getOnItemChildClickListener() {
            return mOnItemChildClickListener;
        }
    
        /**
         * @return The callback to be invoked with an itemChild in this RecyclerView has
         * been long clicked, or null id no callback has been set.
         */
        @Nullable
        public final OnItemChildLongClickListener getmOnItemChildLongClickListener() {
            return mOnItemChildLongClickListener;
        }
    

    设置了4个接口的回调
    在onCreateViewHolder方法中回调了
    bindViewClickListener(baseViewHolder);
    设置单击和回调事件

    private void bindViewClickListener(final BaseViewHolder baseViewHolder) {
            if (baseViewHolder == null) {
                return;
            }
            final View view = baseViewHolder.getConvertView();
            if (view == null) {
                return;
            }
            view.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (getOnItemClickListener() != null && baseViewHolder != null) {
    
                        getOnItemClickListener().onItemClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
                    }
    
                }
            });
            view.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    if (getOnItemLongClickListener() != null && baseViewHolder != null) {
                        return getOnItemLongClickListener().onItemLongClick(BaseQuickAdapter.this, v, baseViewHolder.getLayoutPosition() - getHeaderLayoutCount());
                    }
                    return false;
    
                }
            });
        }
    

    相关文章

      网友评论

      • 向安山:请问一下 那个点击事件 我的BaseQuickAdapter适配器中没有setOnItemClickListener这个方法呢?

      本文标题:BaseQuickAdapter

      本文链接:https://www.haomeiwen.com/subject/luisattx.html