在XRecycleView的基础上添加分组列表(拓展功能)

简介

        众所周知,RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好。我们可以在此基础上开发出丰富的效果。RecycleView之所以如此方便与受欢迎,主要是它近乎于一种插拔式的开发模式,也可以说是类装饰装者模式。

    例如:

1,Recycle.setLayoutMnager();

可以设置GridLayoutManger,LinearLayoutManger(横向,纵向),StaggeredGridLayoutManager

2,Recycle.addItenDecoration();

可以添加装饰,针对item的间隔线或者是一些悬浮分组的设计都可以通过它来完成

3,设置适配器,类似于ListView,可以更换不一样的适配器,显示不一样的布局

        以上只是对RecycleView的简单介绍,本文的重点是XRecycleView,在GitHub上是一个非常受欢迎的RecycleView封装框架XRecycleView链接。该框架对RecycleView的加heardView,footView和上拉刷新,下拉加载都做了很好的封装,非常好用,由于我在开发中有需要做一个分组列表的需求,本身网络上有很多实现分组的适配器,可直接给RecycleView设置。虽然XRecycleView 是基于RecycleView的封装,但测试结果是不可以直接给XRecycleView设置带有分组效果的适配器设置的。原因和XRecycleView的封装有关,具体的可以查看源码理解分析,这里不深入探讨。

思路

分析XRcycleView中添加headerView或者footerView的思路,其实HeaderView,footView都是Item的一种,只不过显示在顶部或者底部的位置,他通过为其设置ItemType来完成的。而且他的下拉刷新本身也是一个头的位置。


上面的代码是判断适配器的位置是不是下拉刷新头,我们可以看到他直接返回position == 0,也就是说0的位置默认为下拉刷新头。


而真正heardView的位置就得position>=1.

那么,我们的分组,其实也可以类似于addHeardView()一样addTitleView();思路基本差不多,但有一些难点。

难点:

1,title的位置是变动的,如果加入多个头,要如何处理他的位置和判断该位置是不是title

2,要如何在title的位置create不一样的布局

3,要如何不打乱展示数据的位置前提下加入title

addTitleView()的处理过程

1,创建必要的几个集合存储操作数据

 //title类型type的基数
    private static final int TYPE_TITLE_POSITION = 10003;
//存放titleView的集合,每一个都是不一样的
    private SparseArray<View> mTitleViews = new SparseArray<>();
//每个title必须有不同的type,不然滚动的时候顺序会变化(TYPE_TITLE_POSITION+(1,··))
    private List<Integer> sTitleTypes = new ArrayList<>();
//存放title位置的集合,就是在适配器的哪个指定位置插入title
    private SparseIntArray titleIndex = new SparseIntArray();

如上面的代码所示,每一个titleView需要不一样的itemType,指定加入的位置。

2,添加TitleView的方法和判断titleType

/**
     * 添加title的方法
     *
     * @param view          加入的view
     * @param key           view对应的key 和 组成view类型的基数
     * @param titlePosition 位置
     */
    public void addTitleView(View view, int key, int titlePosition) {
        sTitleTypes.add(TYPE_TITLE_POSITION + key);
        titleIndex.put(mTitleViews.size(), titlePosition);
        mTitleViews.put(key, view);
        if (mWrapAdapter != null) {
            mWrapAdapter.notifyDataSetChanged();
        }
    }

    /**
     * 数据变化时,需要清空集合数据
     */
    public void clearTitleView() {
        titleIndex.clear();
        sTitleTypes.clear();
        mTitleViews.clear();
    }

    /**
     * 根据Title的ViewType判断是哪个titleView
     *
     * @param itemType
     * @return
     */
    private View getTitleViewByType(int itemType) {
        if (!isTitleType(itemType)) {
            return null;
        }
        return mTitleViews.get(itemType - TYPE_TITLE_POSITION);
    }

    /**
     * 判断一个type是否为TitleType
     *
     * @param itemViewType
     * @return
     */
    private boolean isTitleType(int itemViewType) {
        return mTitleViews.size() > 0 && sTitleTypes.contains(itemViewType);
    }

简单的理解上面的代码,就是

(1),为每一个titleView,指定titleViewType。(TYPE_TITLE_POSITION+key)

(2),为每一个titleView的集合指定唯一的存放位置。(key,View)

(3),为每一个titleView应该被加入到适配器总的具体位置指定集合存放位置(mTitleViews.size(),titlePosition)

之后就是,每次加入需要清除集合数据(位置变动)。根据titleViewType判断是不是titleView的。根据titleViewType返回对应的view

3,关键步骤,在XRcycleView 的内部适配器WrapAdapter中做处理

 /**
         * 根据传入的位置判断是不是加入title的位置
         * @param position
         * @return
         */
        public boolean isTitlePosition(int position) {
            boolean flag = false;
            for (int i = 0; i < titleIndex.size(); i++) {
                flag = (position == titleIndex.get(i));
                if (flag) {
                    break;
                }
            }
            return flag;
        }
  /**
         * 获取title的总数
         * @return
         */
        public int getTitlesCount() {
            return mTitleViews.size();
        }
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == TYPE_REFRESH_HEADER) {
                return new SimpleViewHolder(mRefreshHeader);
            } else if (isHeaderType(viewType)) {
                return new SimpleViewHolder(getHeaderViewByType(viewType));
            } else if (viewType == TYPE_FOOTER) {
                return new SimpleViewHolder(mFootView);
            } else if (isTitleType(viewType)) {
                return new SimpleViewHolder(getTitleViewByType(viewType));
            }
            return adapter.onCreateViewHolder(parent, viewType);
        }

oncreateViewHolder最关键的方法,一定要弄清楚它的调用条件:它是在viewType改变的情况下才会调用。所以上面的添加titleView之前需要指定每一个view的唯一key和清理所有集合数据。然后就是根据不一样的titleViewType,创建对应不一样的view。

 /**
         * some times we need to override this
         * 为了不让适配器适配的数据混乱和丢失,需要对头部view,下拉刷新view和title的view的位置进行处理
         *
         * @param holder
         * @param position
         * @param payloads
         */
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
            if (isHeader(position) || isRefreshHeader(position) || isTitlePosition(position)) {
                return;
            }

            final int tempPosition = position - (getHeadersCount() + 1);
            final int titleCount = titleIndex.size();
            int adjPosition = tempPosition;
            //处理title加入的位置不影响适配器中真实数据的位置排列
            for (int i = 0; i < titleCount; i++) {
                if (position >= titleIndex.get(i) + 1) {
                    adjPosition = tempPosition - i - 1;
                }
            }

            int adapterCount;
            if (adapter != null) {
                adapterCount = adapter.getItemCount();
                if (adjPosition < adapterCount) {
                    if (payloads.isEmpty()) {
                        adapter.onBindViewHolder(holder, adjPosition);
                    } else {
                        adapter.onBindViewHolder(holder, adjPosition, payloads);
                    }
                }
            }
        }
这个是绑定viewHolder的方法,要记住它是每一次滑动展示position上的view时都会被调用,所以对应的加入titleView的位置我们需要返回,因为它要填充的view是不一样的,而真实数据的排列位置受到了它的影响,需要对数据的position做处理。
        @Override
        public int getItemCount() {
            if (loadingMoreEnabled) {
                if (adapter != null) {
                    return getHeadersCount() + getTitlesCount() + adapter.getItemCount() + 2;
                } else {
                    return getHeadersCount() + getTitlesCount() + 2;
                }
            } else {
                if (adapter != null) {
                    return getHeadersCount() + getTitlesCount() + adapter.getItemCount() + 1;
                } else {
                    return getHeadersCount() + getTitlesCount() + 1;
                }
            }
        }

这是适配器中真实的item数量,当有数据的是后,总的数量等于头的数量+itemcount的数量+title的数量+1(下拉刷新头)或者+2(下拉加载foot的位置减了1).

  @Override
        public int getItemViewType(int position) {
            int adjPosition = position - (getHeadersCount() + getTitlesCount() + 1);
            if (isRefreshHeader(position)) {
                return TYPE_REFRESH_HEADER;
            }
            if (isHeader(position)) {
                position = position - 1;
                return sHeaderTypes.get(position);
            }
            if (isFooter(position)) {
                return TYPE_FOOTER;
            }
            if (isTitlePosition(position)) {
                int titleCount = titleIndex.size();
                for (int i = 0; i < titleCount; i++) {
                    if (position == titleIndex.get(i)) {
                        return sTitleTypes.get(i);
                    }
                }
            }
            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 void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
            RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager instanceof GridLayoutManager) {
                final GridLayoutManager gridManager = ((GridLayoutManager) manager);
                gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                    @Override
                    public int getSpanSize(int position) {
                        return (isHeader(position) || isFooter(position) || isRefreshHeader(position)) || isTitlePosition(position)
                                ? gridManager.getSpanCount() : 1;
                    }
                });
            }
            adapter.onAttachedToRecyclerView(recyclerView);
        }

这个方法处理的是GridView的问题,在titleView的位置布局是不一样的,需要合并成一行。

还有一些其他的地方,需要一些对titleView的位置和个数判断,这里就不贴出来了,具体的可在我的百度网盘XrecycleViewExpand 链接:https://pan.baidu.com/s/1Y7iVEr30w87L8XFmbfT6QQ 密码:krg6 下载demo详解。本文主要是在XrecycleView上拓展功能,以此分享和复用,方便以后开发理解。非常诚挚感谢XrecycleView原作者的奉献精神。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值