android recycleView 数据分组显示解决方案

作者: aitality | 来源:发表于2018-08-31 18:05 被阅读185次

最近在开发过程中,遇到这样一个需求,就是对数组分组显示,在网上找了很多方案,但都讲得不全面,在此记录一下自己实践成功的方案。如下图:


S80831-173502.jpg

具体步骤

第一步、使用RecycleView实现,并设置layoutManager为GridLayoutManager。

int column = Utils.calculateCashBackStoreSpanCount(getContext());
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), column);
recycleView.setLayoutManager(layoutManager);

第二步、使adapter支持多类型,例如:
需要加入开源库
versions.base_adapter = "3.0.3"
compile "com.zhy:base-rvadapter:$versions.base_adapter"

public class StoreGridAdapter extends MultiItemTypeAdapter<Object> {
    public static int ITEM_TYPE_ADS = 2;
    public static int ITEM_TYPE_LABEL = 1;
    public static int ITEM_TYPE_STORE = 0;

    public StoreGridAdapter(Context context, List<Object> list) {
        super(context, list);
        addItemViewDelegate(ITEM_TYPE_LABEL, new LabelItemViewDelegate());
        addItemViewDelegate(ITEM_TYPE_STORE, new StoreItemViewDelegate());
        addItemViewDelegate(ITEM_TYPE_ADS, new AdsItemViewDelegate());
    }

    class LabelItemViewDelegate implements ItemViewDelegate<Object> {

        @Override
        public int getItemViewLayoutId() {
            return R.layout.item_store_first_letter_lay;
        }

        @Override
        public boolean isForViewType(Object item, int position) {
            return (item instanceof String);
        }

        @Override
        public void convert(ViewHolder holder, Object item, int position) {
            String label = (String) item;
            TextView tv = holder.getView(R.id.text);
            tv.setText(Formatter.toUpperOfFirst(label));
        }
    }

    class StoreItemViewDelegate implements ItemViewDelegate<Object> {
        @Override
        public int getItemViewLayoutId() {
            return R.layout.item_grid_store_lay;
        }

        @Override
        public boolean isForViewType(Object item, int position) {
            return (item instanceof StoreItem);
        }

        @Override
        public void convert(ViewHolder holder, Object o, int position) {
            final StoreItem storeItem = (StoreItem) o;
            ImageView logoImg = holder.getView(R.id.itemStoreLogo);
            TextView cashBackTxt = holder.getView(R.id.itemCashback);
            CommonImageLoader.displayImage(mContext, storeItem.storeLogo, logoImg);
            cashBackTxt.setText(storeItem.cashbackValue);
            holder.getConvertView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onStoreClick(storeItem);
                }
            });
        }
    }

    class AdsItemViewDelegate implements ItemViewDelegate<Object> {
        @Override
        public int getItemViewLayoutId() {
            return R.layout.layout_ads;
        }

        @Override
        public boolean isForViewType(Object item, int position) {
            return (item instanceof AdGroup);
        }

        @Override
        public void convert(ViewHolder holder, Object o, int position) {
            AdGroup group = (AdGroup) o;
            AdsGroupView adsView = holder.getView(R.id.ads);
            adsView.showAds(group.adGroup);
        }
    }

    //计算每一行显示的列数
    @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 (getItemViewType(position) == ITEM_TYPE_LABEL
                            || getItemViewType(position) == ITEM_TYPE_ADS)
                            ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

    private void onStoreClick(StoreItem storeItem) {
        
    }
}

第三步、计算间距。大概说一下算法:首先识别item是否是label,然后设置label间距;其次计算item所在位置是不是新行开始。代码如下:

class MyItemDecoration extends RecyclerView.ItemDecoration {

        private int space;
        private int spanCount;

        MyItemDecoration (int cols, int space){
            this.spanCount = cols;
            this.space = space;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            int position = parent.getChildAdapterPosition(view); // item position
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            if(position == 0) {
                outRect.top = space;
            }
            if(isAds(parent, position)) {
                outRect.top = 0;
                outRect.left = 0;
                outRect.right = 0;
            }else if(isLabel(parent, position)) {
                //label
                outRect.left = space*2;
                outRect.right = space;
            }else{
                if(isNewLine(parent, position)) {
                    outRect.left = space;
                    outRect.right = space;
                }else{
                    outRect.right = space;
                }
            }
            outRect.bottom = space;
        }

        private boolean isLabel(RecyclerView parent, int position){
            int viewType = parent.getAdapter().getItemViewType(position);
            return (viewType == AllStoreGridAdapter.ITEM_TYPE_LABEL);
        }

        private boolean isNewLine(RecyclerView parent, int position){
            boolean newLine = false;
            if(isLabel(parent, position-1)){
                return true;
            }
            int startIndex = findAreaStartIndex(parent, position);
            if(startIndex > -1) {
                return ((position-1 - startIndex) % spanCount == 0);
            }
            return newLine;
        }

        private int findAreaStartIndex(RecyclerView parent, int position){
            for(int i=position; i<= position; i--){
                if(isLabel(parent, i)){
                    return i;
                }
            }
            return -1;
        }

        private boolean isAds(RecyclerView parent, int position){
            int viewType = parent.getAdapter().getItemViewType(position);
            return (viewType == AllStoreGridAdapter.ITEM_TYPE_ADS);
        }
    }

总结

RecycleView控件可以绘制出各种各样的布局效果,秘诀就是合理使用layoutManager,动态item的带下和间距。

相关文章

网友评论

  • yemoumou:俱往矣,数风流人物,还看今朝。-简书朋友你好,我是币圈一老友,我的写作方向是区块链和数字货币,初到简书,望多多关照。互粉互赞,已赞,期待您的回赞哦。-Қ逥ݗ链剂

本文标题:android recycleView 数据分组显示解决方案

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