ByRecyclerView

Introduction: RecyclerView 下拉刷新、加载更多、分割线、点击/长按、头布局/尾布局/状态布局、极简 adapter(RV/ListView)等
More: Author   ReportBugs   OfficialWebsite   
Tags:

English | 简体中文

jitpack Apache License 2.0 download Maintained

ByRecyclerView 提供了下拉刷新、上拉松手/自动加载更多、 添加 HeaderView/FooterView、setStateView、item 点击/长按、item 局部刷新、万能分割线、粘性 header、极简 Adapter(databinding)等功能。

ByRecyclerView is an Android RecyclerView library for pull-to-refresh, automatic or pull-up load more, headers and footers, empty states, and a simplified adapter. 它适用于 Android View 系统中的列表页面,不是 Jetpack Compose 原生组件。

English summary

  • Pull-to-refresh (also works with SwipeRefreshLayout) and pull-up or automatic load more with a configurable preload distance.
  • Header, footer and state views; setPageData() handles first-page empty states and later-page updates.
  • Simplified adapters (DataBinding supported), item/child click and long-click listeners, payload partial refresh.
  • Item decorations for linear, grid and staggered-grid layouts, sticky headers, skeleton screens.
implementation 'com.github.youlookwhat:ByRecyclerView:1.4.2' // requires the JitPack repository

See README_EN.md for full installation steps and Java/Kotlin examples.

维护状态 / Status

维护中(Actively maintained):最新版本 1.4.2,可通过 JitPack 引用(见上方 JitPack 徽章显示的最新版本)。欢迎在 Issues 反馈问题。

Actively maintained. Latest release: 1.4.2, available via JitPack. Please report problems in Issues.

使用 AI 接入

如果希望 AI 帮你在现有 Android 项目中接入,可以复制下面的提示词,并按实际需求修改。它会引导 AI 阅读 AI 接入指南 AGENTS.md(该文件不会自动加载到其他项目):

请使用 https://github.com/youlookwhat/ByRecyclerView 为当前 Android View 项目的列表实现下拉刷新和自动加载更多。请先阅读仓库的 README 和 AGENTS.md,结合本项目的 Gradle 配置、数据接口和现有代码完成接入,处理空状态、加载失败、重试和没有更多数据,并编译及验证;无法验证的部分请说明。

按实际项目替换需求,例如上拉松手加载、Kotlin 或 Java;Compose 原生列表不适用。完整的安装和 Java/Kotlin 示例见 英文快速开始。

功能特性

  • 1.下拉刷新 / 支持 SwipeRefreshLayout
  • 2.自定义 下拉刷新布局 / 加载更多布局
  • 3.加载更多机制:上拉松手 / 自动加载更多
  • 4.Add HeaderView、FooterView、StateView
  • 5.item 及子 view 的点击/长按事件(防止重复点击)
  • 6.item 局部刷新
  • 7.优化过的 BaseAdapter (RV/LV),减少大量代码
  • 8.Adapter 结合 DataBinding/ViewBinding 使用 (RV/LV)
  • 9.可添加 万能分隔线(线性/宫格/瀑布流)
  • 10.可配置 Skeleton 骨架图
  • 11.仿京东首页 RecyclerView 嵌套滑动置顶示例
  • 12.可配置自动加载更多机制 和 设置预加载条数

最新:setPageData()一行代码处理 列表显示数据和空视图

Document

Screenshots

下载试用

AndroidX 版本(Apk-Demo)

如何使用

建议直接看 示例代码 或 Wiki 文档

快速开始

以下是使用 settings.gradle 管理仓库的项目配置。如果项目仍使用根目录 build.gradle 中的 allprojects.repositories,请将 JitPack 加到那里,不要重复配置。确保同时保留 Google 和 Maven Central 仓库。

// settings.gradle
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://jitpack.io' }
    }
}

Kotlin DSL:

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

在应用模块添加依赖:

dependencies {
    implementation 'com.github.youlookwhat:ByRecyclerView:1.4.2'
    implementation 'androidx.recyclerview:recyclerview:1.3.0'
    implementation 'com.google.android.material:material:1.10.0'
}
// build.gradle.kts
dependencies {
    implementation("com.github.youlookwhat:ByRecyclerView:1.4.2")
    implementation("androidx.recyclerview:recyclerview:1.3.0")
    implementation("com.google.android.material:material:1.10.0")
}

库模块将 RecyclerView 和 Material 声明为 compileOnly,应用需要提供这些依赖;已有依赖时请选用与项目兼容的版本。库模块声明的 minSdk 为 14,实际最低版本还取决于应用使用的依赖。

在 XML 布局中引用 ByRecyclerView:

<me.jingbin.library.ByRecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:layoutManager="LinearLayoutManager"
    tools:listitem="@layout/item_main" />

使用 BaseRecyclerAdapter(示例中的 list 是可修改的 List<String>):

mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
BaseRecyclerAdapter<String> mAdapter = new BaseRecyclerAdapter<String>(R.layout.item_main, list) {
    @Override
    protected void bindView(BaseByViewHolder<String> holder, String bean, int position) {
        holder.setText(R.id.tv_text, bean);
    }
};
mRecyclerView.setAdapter(mAdapter);

mAdapter.setNewData(list);   // 设置第一页数据

设置监听:

// 下拉刷新监听
mRecyclerView.setOnRefreshListener(new ByRecyclerView.OnRefreshListener() {
            @Override
            public void onRefresh() {
                // 刷新完成
                mRecyclerView.setRefreshing(false);
            }
        });
// 加载更多监听
mRecyclerView.setOnLoadMoreListener(new ByRecyclerView.OnLoadMoreListener() {
    @Override
    public void onLoadMore() {
         // 请求下一页后,根据结果选择一种状态:
         // 成功且还有更多:mAdapter.addData(nextPage); mRecyclerView.loadMoreComplete();
         // 没有更多:mRecyclerView.loadMoreEnd();
         // 请求失败:mRecyclerView.loadMoreFail();
    }
});

显示数据:

可直接调用 adapter 的 setPageData(boolean isFirstPage, List<T> data, int emptyLayoutId);必须先通过 setAdapter 关联 ByRecyclerView。例如:

mAdapter.setPageData(true, firstPage, R.layout.layout_empty);  // 首页,空列表时显示空布局
mAdapter.setPageData(false, nextPage, R.layout.layout_empty); // 后续页,空列表表示没有更多

请求失败时不要传空列表来表示错误:刷新失败调用 setRefreshing(false),加载更多失败调用 loadMoreFail(),并在应用中展示错误。完整 Java/Kotlin 示例见 英文快速开始。setPageData 的实现如下:

/**
 * 设置数据 和 处理空视图。
 * 如果想列表上方显示状态视图(StateView),不能使用这个方法。
 *
 * @param isFirstPage 是否是第一页
 * @param data        需要设置的数据
 * @param emptyView   空视图的 View
 */
public void setPageData(boolean isFirstPage, List<T> data, View emptyView) {
    if (mRecyclerView == null) {
        return;
    }
    if (isFirstPage) {
        // 第一页
        if (data != null && data.size() > 0) {
            // 有数据
            mRecyclerView.setStateViewEnabled(false);
            mRecyclerView.setLoadMoreEnabled(true);
            setNewData(data);
        } else {
            // 没数据,设置空布局
            if (emptyView != null) {
                mRecyclerView.setStateView(emptyView);
            }
            mRecyclerView.setLoadMoreEnabled(false);
            setNewData(null);
        }
    } else {
        // 第二页
        if (data != null && data.size() > 0) {
            // 有数据,显示更多数据
            addData(data);
            mRecyclerView.loadMoreComplete();
        } else {
            // 没数据,显示加载到底
            mRecyclerView.loadMoreEnd();
        }
    }
}

自动加载更多/预加载

为了适配更多的加载场景,我们增加了自动加载更多的机制,也可以设置预加载的条数,即滑动到倒数第preLoadNumber 条数据时执行加载更多,相比以前只用在设置监听时加上状态true即可,使用方式:

void setOnLoadMoreListener(boolean isAutoLoadMore, OnLoadMoreListener listener)
void setOnLoadMoreListener(boolean isAutoLoadMore, int preLoadNumber, OnLoadMoreListener listener)

/**
 * 设置加载更多监听
 *
 * @param isAutoLoadMore 是否自动加载
 * @param preLoadNumber  自动加载时,默认滑动到倒数第[preLoadNumber]条数据加载,默认 1
 * @param listener       监听器
 * @param delayMillis    延迟多少毫秒执行加载更多
 */
void setOnLoadMoreListener(boolean isAutoLoadMore, int preLoadNumber, OnLoadMoreListener listener, long delayMillis)
    1. 如果不设置,默认还是使用上拉松手加载更多机制
    1. recyclerView.setLoadMoreEnabled(false) 会关闭加载更多,而不只是关闭自动模式。
    1. 不满一屏不加载更多setNotFullScreenNoLoadMore(),只对上拉松手加载更多有效

ItemDecoration

万能分割线,可给 Linear/Grid/StaggeredGrid 设置,并可配置去除不显示分割线的头部和尾部个数

1.给 LinearLayout 设置分割线

// 选择 1:设置 drawable
SpacesItemDecoration itemDecoration = new SpacesItemDecoration(this, SpacesItemDecoration.VERTICAL)
        .setNoShowDivider(1, 1)  // 第一个参数:头部不显示分割线的个数,第二个参数:尾部不显示分割线的个数,默认为 1
        .setDrawable(R.drawable.shape_line);// 设置 drawable 文件

// 选择 2:设置颜色、高度、间距等
SpacesItemDecoration itemDecoration = new SpacesItemDecoration(this, SpacesItemDecoration.VERTICAL)
        .setNoShowDivider(1, 1)
        // 颜色,分割线间距,左边距(单位 dp),右边距(单位 dp)
        .setParam(R.color.colorBlue, 10, 70, 70);

recyclerView.addItemDecoration(itemDecoration);

2.给宫格/瀑布流设置分割线

// 10:间距; true:距屏幕周围是否也有间距
GridSpaceItemDecoration itemDecoration = new GridSpaceItemDecoration(10, true)
        .setNoShowSpace(1, 1);// 第一个参数:头部不显示分割线的个数,第二个参数:尾部不显示分割线的个数,默认为 1

recyclerView.addItemDecoration(itemDecoration);

add HeaderView/FooterView、setStateView

// 获取 view 对应 databinding,注意:recyclerView.getParent()
LayoutHeaderViewBinding headerBinding = DataBindingUtil.inflate(LayoutInflater.from(this), R.layout.layout_header_view, (ViewGroup) binding.recyclerView.getParent(), false);
recyclerView.addHeaderView(headerBinding.getRoot());

recyclerView.addFooterView(footerView); // 也可传入布局资源 ID
recyclerView.setStateView(stateView);   // 也可传入布局资源 ID

// headerView、footerView、setStateView 支持一键隐藏,设置后需要 notify
recyclerView.setHeaderViewEnabled(false);
recyclerView.setFootViewEnabled(false);
recyclerView.setStateViewEnabled(false);

Item 点击/长按监听

// 防重复点击使用 OnItemFilterClickListener
mRecyclerView.setOnItemClickListener(new ByRecyclerView.OnItemClickListener() {
    @Override
    public void onClick(View v, int position) {
        // 通过 adapter 获取对应 position 的数据
        DataItemBean itemData = mAdapter.getItemData(position);
    }
});
mRecyclerView.setOnItemLongClickListener(new ByRecyclerView.OnItemLongClickListener() {
    @Override
    public boolean onLongClick(View v, int position) {
        return false;
    }
});

// 添加 子 View 的点击/长按事件
holder.addOnClickListener(R.id.tv_text);
holder.addOnLongClickListener(R.id.tv_text);

// 防重复点击使用 OnItemChildFilterClickListener
recyclerView.setOnItemChildClickListener(new ByRecyclerView.OnItemChildClickListener() {
    @Override
    public void onItemChildClick(View view, int position) {
    }
});
recyclerView.setOnItemChildLongClickListener(new ByRecyclerView.OnItemChildLongClickListener() {
    @Override
    public void onItemChildLongClick(View view, int position) {
    }
});

Item 局部刷新

//  设置要局部刷新的 position 及 payload
adapter.refreshNotifyItemChanged(position, PayloadAdapter.PAYLOAD_COLLECT);

// adapter 里额外再继承 bindViewPayloads 方法
@Override
protected void bindViewPayloads(@NonNull BaseBindingHolder holder, @NonNull DataItemBean bean, @NonNull ItemPayloadBinding binding, int position, @NonNull List<Object> payloads) {
    for (Object p : payloads) {
        int code = (int) p;
        switch (code) {
            case PAYLOAD_ZAN:
                binding.tvZan.setText(bean.getIsZan() == 1 ? "已赞" : "点赞");
                break;
            case PAYLOAD_COLLECT:
                binding.tvCollect.setText(bean.getIsCollect() == 1 ? "已收藏" : "收藏");
                break;
            default:
                break;

        }
    }
}

设置 Item 悬浮置顶

// 1、使用 StickyLinearLayoutManager,传入 adapter
StickyLinearLayoutManager layoutManager = new StickyLinearLayoutManager(getContext(), mAdapter);

// 2、在 adapter 里,将悬浮的 item 的 ItemViewType 设置为 StickyHeaderHandler.TYPE_STICKY_VIEW
@Override
public int getItemViewType(int position) {
    if ("title".equals(getItemData(position).getType())) {
        return StickyHeaderHandler.TYPE_STICKY_VIEW;
    } else {
        return 2;
    }
}

设置 Skeleton 骨架图

1.设置 item 骨架图

// 显示
skeletonScreen = BySkeleton
        .bindItem(binding.recyclerView)
        .adapter(mAdapter)// 必须设置 adapter,且在此之前不要设置 adapter
        .shimmer(false)// 是否有动画
        .load(R.layout.layout_by_default_item_skeleton)// item 骨架图
        .angle(30)// 微光角度
        .frozen(false) // 是否不可滑动
        .color(R.color.colorWhite)// 动画的颜色
        .duration(1500)// 微光一次显示时间
        .count(10)// item 个数
        .show();

// 隐藏
skeletonScreen.hide();

2.设置 view 骨架图

// 显示
skeletonScreen = BySkeleton
        .bindView(binding.recyclerView)
        .load(R.layout.layout_skeleton_view)// view 骨架图
        .shimmer(true)// 是否有动画
        .angle(20)// 微光角度
        .color(R.color.colorWhite)// 动画的颜色
        .duration(1500)// 微光一次显示时间
        .show();

// 隐藏
skeletonScreen.hide();

ViewBinding 使用示例

  • 1、由于 DataBinding 和 ViewBinding 最好别同时集成在项目里,所以新建了一个新的项目实践。
  • 2、ViewBinding 对封装的不是很友好,demo 里给的是没有进行反射处理的示例。
  • 3、针对于 ListView 和 RecyclerView 的 adapter 都进行了封装。
  • 4、推荐使用 DataBinding。

具体见项目:ByRv-viewbinding

与 BRVAH、XRecyclerView 对比

ByRecyclerView BRVAH XRecyclerView
下拉刷新布局 继承基类自定义布局 无 只能简单设置样式
SwipeRefreshLayout 可配合使用 可配合使用 不能使用
加载更多布局 继承基类自定义布局 继承基类设置简单布局 继承基类自定义类
加载更多机制 上拉松手/自动加载更多 自动加载更多 上拉松手加载
HeaderView 多 ViewType 区别 同一个 item 多 ViewType 区别
FooterView 同一个 item 同一个 item 不能添加
EmptyView 可设置 可设置 不可设置
点击/长按事件 有 有 无

ByRecyclerView 是 XRecyclerView 的拓展,可完全替换 XRecyclerView,对于 BRVAH 它的区别在于:

  • headerView 使用的是多 type 的形式,即一个 header 就是一个 position
  • 可简单设置 上拉松手/自动加载更多 两种加载方式
  • 自带下拉加载布局,也可使用三方刷新框架,比如 SwipeRefreshLayout
  • 万能分割线(LinearLayout / GridLayout / StaggeredGridLayout)
  • 可设置 Skeleton 骨架图

混淆

此资源库没有使用到任何序列化、反序列化、JNI、反射,无需进行额外的混淆操作,并且已经测试通过,在实际项目中使用,如果你在项目混淆之后出现问题,请及时联系我。

Issues

如果有任何问题,请到 GitHub 的issue 处写上你不明白的地方,也可以通过下面提供的方式联系我,我会及时给予帮助。如果此库帮助到了你,欢迎点个 star,非常感谢!

Thanks

About me

License

Copyright (C) 2016 Bin Jing

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest