搭配BRVAH高效使用RecyclerView

本文介绍了如何利用BRVAH高效地使用RecyclerView,包括实现列表加载动画、复杂布局、拖拽滑动删除、监听事件以及添加Section头部视图等。详细讲解了BRVAH的功能和配置,以及在实际应用中的各种场景和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习目标

熟悉使用 BRVAH 解决对应各种 adapter 需求

概述

BRVAH 是 Github 上的一个很棒的开源项目,主要作用是帮助我们更加高效的使用 Recyclerview 控件,处理项目中常见需求的 Adapter,使用起来非常方便,更多介绍可去BRVAH官网查看。

BRVAH 主要是针对 Adapter 来设计的。

BRVAH 为我们提供了一般情况下的BaseQuickAdapter,和几个特定需求下的Adapter,BaseMultiItemQuickAdapter用于复杂类布局列表;BaseItemDraggableAdapter 用于拖拽移动和滑动删除类列表; BaseSectionQuickAdapter用于带 Section 头部 View 的列表。

build.gradle 配置说明

添加资源库

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}

添加依赖

dependencies {
        compile 'com.github.CymChad:BaseRecyclerViewAdapterHelper:VERSION_CODE'
}

VERSION_CODE的最新版本可以参考这里

功能概要说明

1. 实现Recyclerview的列表加载动画效果

效果图1

我们只需将自建的 xxAdapter 继承 BRVAH 对应满足需求的 Adapter,然后在 Activity 中实例化,通过openLoadAnimation() 方法完成特定的动画效果。

BRVAH 支持 5 种动画,BaseQuickAdapter.ALPHAIN淡入、BaseQuickAdapter.SLIDEIN_BOTTOM从底部入、BaseQuickAdapter.SLIDEIN_LEFT从左边进入、BaseQuickAdapter.SLIDEIN_RIGHT从右边进入和自定义动画。

关于自定义的动画,可以通过实现 BaseAnimation 这个类,重写
getAnimators(View view) 方法来完成自定义动画。

2. 实现Recyclerview的复杂布局列表

在实际应用中经常会遇到各种样式的列表、宫格和列表同时存在、分类列表等情况。

2.1 Recyclerview 多样式 item 排列的效果

效果图2.1

对于多样式的列表,根据需求创建 type 实体类实现 MultiItemEntity。 xxAdapter继承 BaseMultiItemQuickAdapter类,在构造方法中调用addItemType ()方法加入定义的 itemType 和对应布局,在 Activity 中实例化即可。

2.2 Recyclerview 宫格和列表的混排样式

效果图2.2

关于 Grid 和 List 的混排样式,Grid 样式是一行有多个,而 List 样式是一行只有一个。我们可以把 List 样式看成是 Grid 样式,它就相当于把一个 Grid 的 item 拉长了的样子。

列表与网格混排的布局效果,我们可以创建 xxAdapter 继承 BaseMultiItemQuickAdapter 添加对应 item 类型的布局文件,在 Activity 中创建 GridLayoutManager 对象,设置 spanSize 属性,通过 Adapter 的 setSpanSizeLookup 方法设置每种 item 类型对应的 spanSize。设置 Recyclerview 的 addItemDecoration() 方法设置添加分割线或设置 item 间距。

代码片段:

...
gridLayoutManager = new GridLayoutManager(this,3);
        mRecyclerView.setLayoutManager(gridLayoutManager);
        mAdapter = new GridManagerAdapter(getDataType());
        // 设置横跨度
        // 1:就是横跨1/3
        // 2:就是横跨2/3
        // 3:就是横跨一整行
        mAdapter.setSpanSizeLookup(new BaseQuickAdapter.SpanSizeLookup() {
            @Override
            public int getSpanSize(GridLayoutManager gridLayoutManager, int position) {
                return getDataType().get(position).getSpanSize();
            }
        });
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                super.getItemOffsets(outRect, view, parent, state);
                GridLayoutManager.LayoutParams layoutParams = (GridLayoutManager.LayoutParams)
                        view.getLayoutParams();
                int spanSize = layoutParams.getSpanSize();
                int spanIndex = layoutParams.getSpanIndex();// 从左到右 0~
                if (spanSize == gridLayoutManager.getSpanCount()) {
                    outRect.top = 20;
                }
            }
        });
    // 假设数据
    private List<MultipleItem> getDataType() {
        int type = 0;
        for (int i = 0;i < 13;i++) {
            if (i < 9) {
                type = 1;
            }  else {
                type = 2;
            }
            types.add(i,type);
        }
        List<MultipleItem> list = new ArrayList<>();
        for (int j = 0;j < types.size();j++) {
            if (types.get(j) == 1) {
                list.add(new MultipleItem(1,1));
            } else if (types.get(j) == 2) {
                list.add(new MultipleItem(2,3));
            }
        }
        return list;
    }
...

在创建 GridLayoutManager 对象时,有一个 spanSize 的参数需要设置,它的作用就是使原来一个 item 占满一行变为可以最多三个 item 占满一行。

而设置 setSpanSizeLookup()方法返回的是对应每种 item 类型返回具体的横跨大小。比如上面代码中 TYPE_GRID 类型的 item 在设置的 spanSize 是 1,而 GridLayoutManager 设置的 spanSize 是 3,那么该类型的 item 就会以 3 个 item 占满一行,相当于每个 item 占一行的 1/3。

在我们使用 addItemDecoration() 添加分割线的方法中对这种混排的列表设置 item 间距的时候,在 getItemOffsets() 方法里,通过 GridLayoutManager.LayoutParams 获取 spanSize 来确定 item 类型设置对应间距,其中 spanIndex 表示当前行 item 对应的下标位置,从左到右依次从 0 开始。

2.3 Recyclerview 嵌套 Recyclerview 的复杂布局

效果图2.3.gif

简单的分析下上图的界面布局样式,最外面一个RecyclerView,它里面的每个 item 的布局样式,最上面用 Banner 轮播图控件,下面是各种样式 item 列表的形式,其中有一个可左右滑动的 item,它是嵌套的一个 RecyclerView。

使用 BaseMultiItemQuickAdapter 在重写的 convert() 方法中实例化子 Recyclerview,同样的步骤在进行子 Recyclerview 设置,需要注意的是Recyclerview 嵌套 Recyclerview 会出现子 Recyclerview 抢焦点的问题,导致界面顶部部分控件被挤出,只需在最外面的 Recyclerview 的包裹容器中设置属性 android:descendantFocusability="blocksDescendants" 即可。通过 BRVAH 的 addHeaderView() 方法设置头部布局。

代码片段:

@Override
    protected void convert(BaseViewHolder helper, MultiItemEntity item) {
        switch (helper.getItemViewType()) {
           ...
            case MultipleItem.DOUBANTIME:
                RecyclerView recyclerView = helper.getView(R.id.recyclerview_item_view);
                recyclerView.setLayoutManager(new LinearLayoutManager(mContext,
                        LinearLayoutManager.HORIZONTAL,false));
                recyclerView.setHasFixedSize(true);
                recyclerView.setAdapter(new RrecyclerViewAdapter(DataServer.getReItemData()));
                break;
        }
    }

2. 实现Recyclerview拖拽滑动删除

效果图2.gif

添加 RecyclerView 的拖拽和滑动移除很简单,只需 xxAdapter 继承 BaseItemDraggableAdapter 类,在 Activity 中,添加 OnItemDragListenerOnItemSwipeListener 两个接口,通过 xxAdapter 配置基本属性即可。

代码片段:

OnItemDragListener listener = new OnItemDragListener() {
            @Override
            public void onItemDragStart(RecyclerView.ViewHolder viewHolder, int pos) {
                // item 拖拽的起始位置
                Log.d(TAG,"drag start");
            }
            @Override
            public void onItemDragMoving(RecyclerView.ViewHolder source, int from, RecyclerView.ViewHolder target, int to) {
                 // item 拖拽的过程位置变化
                Log.d(TAG, "move from: " + source.getAdapterPosition() + " to: " + target.getAdapterPosition());
            }
            @Override
            public void onItemDragEnd(RecyclerView.ViewHolder viewHolder, int pos) {
                 // item 拖拽的终点位置
                Log.d(TAG, "drag end");
            }
        };

OnItemSwipeListener onItemSwipeListener = new OnItemSwipeListener() {
            @Override
            public void onItemSwipeStart(RecyclerView.ViewHolder viewHolder, int pos) {
                 // item 滑动的起始位置
                Log.d(TAG, "view swiped start: " + pos);
            }
            @Override
            public void clearView(RecyclerView.ViewHolder viewHolder, int pos) {
                 // item 滑动后的恢复位置
                Log.d(TAG, "View reset: " + pos);
            }
            @Override
            public void onItemSwiped(RecyclerView.ViewHolder viewHolder, int pos) {
                 // item 滑动的移除位置
                Log.d(TAG, "View Swiped: " + pos);
            }
            @Override
            public void onItemSwipeMoving(Canvas canvas, RecyclerView.ViewHolder viewHolder,
                                          float dX, float dY, boolean isCurrentlyActive) {
                 // item 滑动的过程
                canvas.drawColor(ContextCompat.getColor(ItemDragAndSwipUseActivity.this,R.color.colorKbtt));
            }
        };
ItemDragAndSwipeCallback itemDragAndSwipeCallback = new ItemDragAndSwipeCallback(mAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemDragAndSwipeCallback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);

mItemDragAndSwipeCallback.setSwipeMoveFlags(ItemTouchHelper.START | ItemTouchHelper.END);
        // open slide to delete
        mAdapter.enableSwipeItem();
        mAdapter.setOnItemSwipeListener(onItemSwipeListener);
        // open drag
        mAdapter.enableDragItem(itemTouchHelper);
        mAdapter.setOnItemDragListener(listener);

        mRecyclerView.setAdapter(mAdapter);

3. 实现Recyclerview监听事件

效果图3.gif

BRVAH为我们提供好了全面的 item 和 item 子 View 的监听事件,我们只需在继承 BRVAH 提供的 Adapter 的基础上,通过 xxAdapter 来调用对应的监听,只是设置监听子 View 前,我们需要在 xxAadapter 中对应的 item 的子 View 注册对应的监听事件。

3.1 对于 item 的监听事件处理

直接在 Activity 中添加
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();
            }
        });

item 长按监听事件

adapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) {
                Log.d(TAG, "onItemLongClick: ");
                Toast.makeText(ItemClickActivity.this, "onItemLongClick" + position, Toast.LENGTH_SHORT).show();
                return false;
            }
        });

3.2 对 item 中子 View 的监听事件处理

首先需要在 xxAdapter 中注册子 View 的监听事件。
点击监听事件

 @Override
    protected void convert(BaseViewHolder viewHolder, Status item) {
        viewHolder.setText(R.id.tweetName, item.getUserName())
                .setText(R.id.tweetText, item.getText())
                .setText(R.id.tweetDate, item.getCreatedAt())
                .setVisible(R.id.tweetRT, item.isRetweet())
                .addOnClickListener(R.id.tweetAvatar)
                .addOnClickListener(R.id.tweetName)
                .linkify(R.id.tweetText);

    }

长按监听事件

 @Override
    protected void convert(BaseViewHolder helper, Status item) {
        helper.setText(R.id.tweetName, item.getUserName())
                .setText(R.id.tweetText, item.getText())
                .setText(R.id.tweetDate, item.getCreatedAt())
                .setVisible(R.id.tweetRT, item.isRetweet())
                .addOnLongClickListener(R.id.tweetText)
                .linkify(R.id.tweetText);

    }

然后在 Activity 中设置对应的子 View 监听事件
点击事件

adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public boolean onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                Log.d(TAG, "onItemChildClick: ");
                Toast.makeText(ItemClickActivity.this, "onItemChildClick" + position, Toast.LENGTH_SHORT).show();
                return false;
            }
        });

子 View 长按事件

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

4. 实现Recyclerview 刷新

效果图4.jpeg

列表下拉刷新和上拉加载是最常用的,BRVAH 虽然也提供了刷新功能,但只是上拉加载更多,而且样式也是固定的,实现下拉刷新还得用 SwipeRefreshLayout 来完成,这也容易理解,因为 BRVAH 主要是对 Adapter 的服务,所以这里只是简单说下 BRVAH 有关上拉加载更多的使用。

在按照 BRVAH 设置完 xxAdapter 后,在 Activity 中,让类实现 BaseQuickAdapter.RequestLoadMoreListener 接口,会重写 onLoadMoreRequested() 方法,在方法中调用 mAdapter.addData() 来添加新的数据,接着设置 mAdapter.loadMoreComplete(),在数据都加载完后设置 mAdapter.loadMoreEnd(false) 显示数据加载完毕。

@Override
    public void onLoadMoreRequested() {
        mSwipeRefreshLayout.setEnabled(false);
        if (loadCount < 2) {
            final  List<MultipleItem> data = DataServer.getRefreshItemData(getServerData());
            mAdapter.addData(data);
            mAdapter.loadMoreComplete();
            loadCount+=1;
        } else {
            mAdapter.loadMoreEnd(false); // 显示加载完毕
        }
        // 设置下拉刷新实效
        mSwipeRefreshLayout.setEnabled(true);
    }

还有就是在下拉刷新的时候,使上拉加载更多失效。

mAdapter.setEnableLoadMore(true);

5. 实现Recyclerview 添加 Section 头部 View

效果图5.png

设置 MySection 类继承 SectionEntity,创建不同的构造方法来设置 item 是否有 header。在 adapter 中,增加了 convertHead() 方法来加载 head 数据。在 Activity 中根据数据创建不同的 MySection 对象加入集合,设置给 adapter。

public class MySection  extends SectionEntity<String>{
    private boolean isMore;
    public MySection(boolean isHeader, String header,boolean isMore) {
        super(isHeader, header);
        this.isMore = isMore;
    }
    public MySection(String s) {
        super(s);
    }
    public void setMore(boolean more) {
        isMore = more;
    }
}

在 adapter 中,构造方法需要传入两个不同的布局 id,第一个是 item 的 layout id,第二个是 head,item 的数据加载在 convert() 方法中,head 的数据加载在 convertHead() 方法中。

public class SectionAdpater extends BaseSectionQuickAdapter<MySection,BaseViewHolder> {

    public SectionAdpater(int layoutResId, int sectionHeadResId, List<MySection> data) {
        super(layoutResId, sectionHeadResId, data);
    }
    // 加载 head 数据
    @Override
    protected void convertHead(BaseViewHolder helper, MySection item) {
        helper.setText(R.id.title_tv,item.header);
        helper.addOnClickListener(R.id.more_btn);
    }
     // 加载 item 数据
    @Override
    protected void convert(BaseViewHolder helper, MySection item) {

    }
}

在 Activity 中

...
// 部分代码
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));
        mData = getData(3);
SectionAdpater sectionAdpater = new SectionAdpater(R.layout.item_section_content,
                R.layout.def_section_head, mData);

...
    // 假数据
    private List<MySection> getData(int size) {
        ArrayList<MySection> data = new ArrayList<>(size);
        data.add(new MySection(true,"1",true));
        for (int i =0;i < size;i++) {
            data.add(new MySection("haha"));
        }
        data.add(new MySection(true,"2",true));
        for (int i =0;i < size;i++) {
            data.add(new MySection("haha"));
        }
        data.add(new MySection(true,"3",true));
        for (int i =0;i < size;i++) {
            data.add(new MySection("haha"));
        }
        return data;
    }

根据数据确定不同的样式,用不同的构造方法设置 item 布局。

重要参考:BRVAH

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值