美文网首页
关于XRecyclerView的解析...

关于XRecyclerView的解析...

作者: i冰点 | 来源:发表于2017-04-04 22:42 被阅读5069次

    XRecyclerView是对RecyclerView的封装,主要完成了下拉刷新、上拉加载更多、添加RecyclerView头部、显示空数据页面的功能,项目地址是Github---XRecyclerView。基本的使用,文档里面有,就不多说了,这里主要记录的是它怎么实现的。

    XRecyclerView的类图

    XRecyclerView的类图

    1、关于WrapAdapter

    1、创建ViewHolder(onCreateViewHolder())
    为了显示不同类型的Item(HeaderViews、RefreshHeader、FootView),对传入的adapter做了封装:如果itemType是TYPE_REFRESH_HEADER、TYPE_FOOTER、HeaderType,创建SimpleViewHolder类型的ViewHolder进行展示,否则调用adapter.onCreateViewHolder()方法,交给子类创建。

    2、绑定数据(onBindViewHolder())
    需要排除HeaderViews、RefreshHeader、FootView的情况,调整position,调用adapter.onBindViewHolder()方法,让子类绑定。

    3、计算itemType、itemCount等等

    大概如下

       private WrapAdapter mWrapAdapter;
    
        @Override
        public void setAdapter(Adapter adapter) {
            //对传入的adapter做封装
            mWrapAdapter = new WrapAdapter(adapter);
            super.setAdapter(mWrapAdapter);
            adapter.registerAdapterDataObserver(mDataObserver);
            mDataObserver.onChanged();
        }
    
        private class WrapAdapter extends RecyclerView.Adapter<ViewHolder> {
            private RecyclerView.Adapter adapter;
            public WrapAdapter(RecyclerView.Adapter adapter) {
                this.adapter = adapter;
            }
            ...
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                if (viewType == TYPE_REFRESH_HEADER) {
                    return new SimpleViewHolder(mRefreshHeader);
                } else if (isHeaderType(viewType)) {
                    //根据viewtype从对应的ArrayList中取出view来展示
                    return new SimpleViewHolder(getHeaderViewByType(viewType));
                } else if (viewType == TYPE_FOOTER) {
                    return new SimpleViewHolder(mFootView);
                }
                return adapter.onCreateViewHolder(parent, viewType);
            }
    
            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                if (isHeader(position) || isRefreshHeader(position)) {
                    return;
                }
                int adjPosition = position - (getHeadersCount() + 1);
                int adapterCount;
                if (adapter != null) {
                    adapterCount = adapter.getItemCount();
                    if (adjPosition < adapterCount) {
                        adapter.onBindViewHolder(holder, adjPosition);
                    }
                }
            }
    
            @Override
            public int getItemCount() {
                if(loadingMoreEnabled) {
                    if (adapter != null) {
                        return getHeadersCount() + adapter.getItemCount() + 2;
                    } else {
                        return getHeadersCount() + 2;
                    }
                }else {
                    if (adapter != null) {
                        return getHeadersCount() + adapter.getItemCount() + 1;
                    } else {
                        return getHeadersCount() + 1;
                    }
                }
            }
    
            @Override
            public int getItemViewType(int position) {
                int adjPosition = position - (getHeadersCount() + 1);
                if (isRefreshHeader(position)) {
                    return TYPE_REFRESH_HEADER;
                }
                if (isHeader(position)) {
                    position = position - 1;
                    return sHeaderTypes.get(position);
                }
                if (isFooter(position)) {
                    return TYPE_FOOTER;
                }
                int adapterCount;
                if (adapter != null) {
                    adapterCount = adapter.getItemCount();
                    if (adjPosition < adapterCount) {
                        int type =  adapter.getItemViewType(adjPosition);
                        if(isReservedItemViewType(type)) {
                            throw new IllegalStateException("XRecyclerView require itemViewType in adapter should be less than 10000 " );
                        }
                        return type;
                    }
                }
                return 0;
            }
    
            @Override
            public long getItemId(int position) {
                if (adapter != null && position >= getHeadersCount() + 1) {
                    int adjPosition = position - (getHeadersCount() + 1);
                    if (adjPosition < adapter.getItemCount()) {
                        return adapter.getItemId(adjPosition);
                    }
                }
                return -1;
            }
    
            private class SimpleViewHolder extends RecyclerView.ViewHolder {
                public SimpleViewHolder(View itemView) {
                    super(itemView);
                }
            }
            ...
        }
    
    
    

    2、添加HeaderViews

    XRecyclerView 对外提供addHeaderView()方法添加header,其中使用了两个ArrayList存储不同的view和viewType。此时,通知mWrapAdapter,刷新界面。

        //HEADER_INIT_INDEX是保留值(ReservedItemViewType),如果用户的adapter与它们重复将会强制抛出异常。
        private static final int HEADER_INIT_INDEX = 10002;
        private ArrayList<View> mHeaderViews = new ArrayList<>();
        //每个header必须有不同的type,不然滚动的时候顺序会变化
        private static List<Integer> sHeaderTypes = new ArrayList<>();
    
        public void addHeaderView(View view) {
            sHeaderTypes.add(HEADER_INIT_INDEX + mHeaderViews.size());
            mHeaderViews.add(view);
            if (mWrapAdapter != null) {
                //刷新界面
                mWrapAdapter.notifyDataSetChanged();
            }
        }
    

    3、空白数据处理

    主要通过AdapterDataObserver 监听数据变化,并以此来更换布局(控制EmptyView、XRecyclerView的显示和隐藏),EmptyView是外部传进来的View。

       
        public void setEmptyView(View emptyView) {
            this.mEmptyView = emptyView;
            mDataObserver.onChanged();
        }
    
        private class DataObserver extends RecyclerView.AdapterDataObserver {
            @Override
            public void onChanged() {
                if (mWrapAdapter != null) {
                    mWrapAdapter.notifyDataSetChanged();
                }
                if (mWrapAdapter != null && mEmptyView != null) {
                    int emptyCount = 1 + mWrapAdapter.getHeadersCount();
                    if (loadingMoreEnabled) {
                        emptyCount++;
                    }
                    if (mWrapAdapter.getItemCount() == emptyCount) {
                        mEmptyView.setVisibility(View.VISIBLE);
                        XRecyclerView.this.setVisibility(View.GONE);
                    } else {
    
                        mEmptyView.setVisibility(View.GONE);
                        XRecyclerView.this.setVisibility(View.VISIBLE);
                    }
                }
            }
            ...
        }
    

    4、下拉刷新

    下拉刷新分为两部分:手指滑动,refreshHeader慢慢显示出来(而且显示的大小跟滑动距离有关);释放后刷新界面、再慢慢隐藏。需要重写onTouchEvent,主要代码如下

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (mLastY == -1) {
                mLastY = ev.getRawY();
            }
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mLastY = ev.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    final float deltaY = ev.getRawY() - mLastY;
                    mLastY = ev.getRawY();
                    if (isOnTop() && pullRefreshEnabled) {
                        //手指滑动距离的1/3,作为RefreshHeader显示的高度
                        mRefreshHeader.onMove(deltaY / DRAG_RATE);
                        if (mRefreshHeader.getVisibleHeight() > 0 && mRefreshHeader.getState() < IRefreshHeader.STATE_REFRESHING) {
                            return false;
                        }
                    }
                    break;
                default:
                    mLastY = -1; // reset
                    if (isOnTop() && pullRefreshEnabled) {
                        //在releaseAction()中处理了手指释放后,RefreshHeader刷新和慢慢向上隐藏的动作
                        if (mRefreshHeader.releaseAction()) {
                            if (mLoadingListener != null) {
                                mLoadingListener.onRefresh();
                            }
                        }
                    }
                    break;
            }
            return super.onTouchEvent(ev);
        }
    
        private boolean isOnTop() {
            if (mRefreshHeader.getParent() != null) {
                return true;
            } else {
                return false;
            }
        }
    
        //RefreshHeader慢慢向上隐藏
        public void refreshComplete() {
            mRefreshHeader.refreshComplete();
        }
       
    

    其中,判断是否在顶部,使用的是mRefreshHeader.getParent(),因为ViewHolder是被复用的,一屏里最多有N+2个ViewHolder,如果recycleVIew没有在顶部,它的ViewHolder可能被其他的item复用了,所以mRefreshHeader.getParent()==null

    下拉刷新

    5、上拉加载更多

    参考:拆解轮子之XRecyclerView


    不喜勿喷

    相关文章

      网友评论

          本文标题:关于XRecyclerView的解析...

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