Android中RecyclerView实现分页滚动的方法详解
发布时间 - 2026-01-11 00:48:44 点击率:次一、需求分析

最近公司项目要实现一个需求要满足以下功能:
1)显示一个 list 列表, item 数量不固定。
2)实现翻页功能,一次翻一页。
3)实现翻至某一页功能。
下面介绍通过 RecyclerView 实现该需求的实现过程(效果图如下)。
二、功能实现
2.1 OnTouchListener 记录当前开始滑动位置
要实现翻页滑动首先我们要确定是向前翻页还是向后翻页,这里通过记录开始翻页前当前的位置和滑动后的位置比较即可得知,下面选择手指触摸按下时滑动的位置为当前开始滑动位置:
//当前滑动距离
private int offsetY = 0;
private int offsetX = 0;
//按下屏幕点
private int startY = 0;
private int startX = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
//手指按下的时候记录开始滚动的坐标
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//手指按下的开始坐标
startY = offsetY;
startX = offsetX;
}
return false;
}
}
好了,当我们确定了滑动方向,下面要考虑的就是如何实现滑动?
2.2 scrollTo(int x, int y) 和 scrollBy(int x, int y) 实现滑动
滑动我们最容易想到的方法就是 scrollTo(int x, int y) 和 scrollBy(int x, int y) 这两个方法, scrollTo(int x, int y) 是将当前 View 的内容滑动至某一位置, scrollBy(int x, int y) 是将当前 View 内容相对于当前位置滑动一定的距离,其实 scrollBy(int x, int y) 内部是调用了 scrollTo(int x, int y) 方法实现的 一开始想用 scrollTo(int x, int y) 去实现,但是简单看了下源码发现, RecyclerView 不支持这个方法:
@Override
public void scrollTo(int x, int y) {
Log.w(TAG, "RecyclerView does not support scrolling to an absolute position. "
+ "Use scrollToPosition instead");
}
所以这里我们就选择使用 scrollBy(int x, int y) 去实现滑动。
2.3 OnFlingListener 和 OnScrollListener 调用滑动时机
上面我们决定使用 scrollBy(int x, int y) 去实现滑动,那么现在我们就要确定 scrollBy(int x, int y) 这个方法的调用时机,我们知道当我们滑动 RecyclerView 的时候一般分为两种情况,一种是手指在屏幕上面缓慢滑动(Scroll),另一种是飞速滑动(onFling),经过一番查阅,发现 RecyclerView 中有这两种状态的监听,那么我们一起看一下这两种状态的方法定义,先看 onFling(int velocityX, int velocityY) :
Note: 由于使用了 RecyclerView 的 OnFlingListener,所以 RecycleView 的版本必须要 recyclerview-v7:25.0.0 以上。
/**
* This class defines the behavior of fling if the developer wishes to handle it.
* <p>
* Subclasses of {@link OnFlingListener} can be used to implement custom fling behavior.
*
* @see #setOnFlingListener(OnFlingListener)
*/
public static abstract class OnFlingListener {
/**
* Override this to handle a fling given the velocities in both x and y directions.
* Note that this method will only be called if the associated {@link LayoutManager}
* supports scrolling and the fling is not handled by nested scrolls first.
*
* @param velocityX the fling velocity on the X axis
* @param velocityY the fling velocity on the Y axis
*
* @return true if the fling washandled, false otherwise.
*/
public abstract boolean onFling(int velocityX, int velocityY);
}
方法的注释写的也很清楚,当这个方法被调用并且返回 true 的时候系统就不处理滑动了,而是将滑动交给我们自己处理。所以我们可以监听这个方法,当我们执行快速滑动的时候在这个方法里面计算要滑动的距离并执行 scrollBy(int x, int y) 实现滑动,然后直接返回 true,表示滑动我们自己处理了,不需要系统处理。
处理代码如下:
//当前滑动距离
private int offsetY = 0;
private int offsetX = 0;
//按下屏幕点
private int startY = 0;
private int startX = 0;
//最后一个可见 view 位置
private int lastItemPosition = -1;
//第一个可见view的位置
private int firstItemPosition = -2;
//总 itemView 数量
private int totalNum;
@Override
public boolean onFling(int velocityX, int velocityY) {
if (mOrientation == ORIENTATION.NULL) {
return false;
}
//获取开始滚动时所在页面的index
int page = getStartPageIndex();
//记录滚动开始和结束的位置
int endPoint = 0;
int startPoint = 0;
//如果是垂直方向
if (mOrientation == ORIENTATION.VERTICAL) {
//开始滚动位置,当前开始执行 scrollBy 位置
startPoint = offsetY;
if (velocityY < 0) {
page--;
} else if (velocityY > 0) {
page++;
} else if (pageNum != -1) {
if (lastItemPosition + 1 == totalNum) {
mRecyclerView.scrollToPosition(0);
}
page = pageNum - 1;
}
//更具不同的速度判断需要滚动的方向
//一次滚动一个 mRecyclerView 高度
endPoint = page * mRecyclerView.getHeight();
} else {
startPoint = offsetX;
if (velocityX < 0) {
page--;
} else if (velocityX > 0) {
page++;
} else if (pageNum != -1) {
if (lastItemPosition + 1 == totalNum) {
mRecyclerView.scrollToPosition(0);
}
page = pageNum - 1;
}
endPoint = page * mRecyclerView.getWidth();
}
//使用动画处理滚动
if (mAnimator == null) {
mAnimator = ValueAnimator.ofInt(startPoint, endPoint);
mAnimator.setDuration(300);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int nowPoint = (int) animation.getAnimatedValue();
if (mOrientation == ORIENTATION.VERTICAL) {
int dy = nowPoint - offsetY;
if (dy == 0) return;
//这里通过RecyclerView的scrollBy方法实现滚动。
mRecyclerView.scrollBy(0, dy);
} else {
int dx = nowPoint - offsetX;
mRecyclerView.scrollBy(dx, 0);
}
}
});
mAnimator.addListener(new AnimatorListenerAdapter() {
//动画结束
@Override
public void onAnimationEnd(Animator animation) {
//回调监听
if (null != mOnPageChangeListener) {
mOnPageChangeListener.onPageChange(getPageIndex());
}
//滚动完成,进行判断是否滚到头了或者滚到尾部了
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
//判断是当前layoutManager是否为LinearLayoutManager
// 只有LinearLayoutManager才有查找第一个和最后一个可见view位置的方法
if (layoutManager instanceof LinearLayoutManager) {
LinearLayoutManager linearManager = (LinearLayoutManager) layoutManager;
//获取最后一个可见view的位置
lastItemPosition = linearManager.findLastVisibleItemPosition();
//获取第一个可见view的位置
firstItemPosition = linearManager.findFirstVisibleItemPosition();
}
totalNum = mRecyclerView.getAdapter().getItemCount();
if (totalNum == lastItemPosition + 1) {
updateLayoutManger();
}
if (firstItemPosition == 0) {
updateLayoutManger();
}
}
});
} else {
mAnimator.cancel();
mAnimator.setIntValues(startPoint, endPoint);
}
mAnimator.start();
return true;
}
}
再看 OnScrollListener 滚动监听方法:
public abstract static class OnScrollListener {
/**
* Callback method to be invoked when RecyclerView's scroll state changes.
*
* @param recyclerView The RecyclerView whose scroll state has changed.
* @param newState The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
* {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
*/
public void onScrollStateChanged(RecyclerView recyclerView, int newState){}
/**
* Callback method to be invoked when the RecyclerView has been scrolled. This will be
* called after the scroll has completed.
* <p>
* This callback will also be called if visible item range changes after a layout
* calculation. In that case, dx and dy will be 0.
* 滚动完成调用
* @param recyclerView The RecyclerView which scrolled.
* @param dx The amount of horizontal scroll.
* @param dy The amount of vertical scroll.
*/
public void onScrolled(RecyclerView recyclerView, int dx, int dy){}
}
这个监听类主要有两个方法一个是 onScrollStateChanged(RecyclerView recyclerView, int newState) 滚动状态发生变化调用, onScrolled(RecyclerView recyclerView, int dx, int dy) RecyclerView 发生滚动和滚动完成调用。有了这两个监听,当我们进行缓慢滑动我们就可以在 onScrollStateChanged(RecyclerView recyclerView, int newState) 中监听滑动如果结束并且超过一定距离去执行翻页,而通过 onScrolled(RecyclerView recyclerView, int dx, int dy) 方法可以记录当前的滑动距离。
处理方法如下:
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
//如果滑动停止
if (newState == RecyclerView.SCROLL_STATE_IDLE && mOrientation != ORIENTATION.NULL) {
boolean move;
int vX = 0, vY = 0;
if (mOrientation == ORIENTATION.VERTICAL) {
int absY = Math.abs(offsetY - startY);
//如果滑动的距离超过屏幕的一半表示需要滑动到下一页
move = absY > recyclerView.getHeight() / 2;
vY = 0;
if (move) {
vY = offsetY - startY < 0 ? -1000 : 1000;
}
} else {
int absX = Math.abs(offsetX - startX);
move = absX > recyclerView.getWidth() / 2;
if (move) {
vX = offsetX - startX < 0 ? -1000 : 1000;
}
}
//调用滑动
mOnFlingListener.onFling(vX, vY);
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//滚动结束记录滚动的偏移量
//记录当前滚动到的位置
offsetY += dy;
offsetX += dx;
}
}
到这里我们要实现滑动的方法和时机基本就搞定了,剩下的就是滑动位置计算和滑动效果实现,滑动位置计算就是一次滑动一整页,这个没什么可说的,所以简单说下实现弹性滑动效果。
2.4 ValueAnimator 实现弹性滑动效果
我们知道如果我们直接调用 scrollBy(int x, int y) 这个方法去滑动,那么是没有缓慢滑动的效果,看着有点愣,所以这里我们通过 ValueAnimator 这个类来实现缓慢滑动的效果,这个就很简单了,直接贴代码:
if (mAnimator == null) {
mAnimator = ValueAnimator.ofInt(startPoint, endPoint);
mAnimator.setDuration(300);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int nowPoint = (int) animation.getAnimatedValue();
if (mOrientation == ORIENTATION.VERTICAL) {
int dy = nowPoint - offsetY;
if (dy == 0) return;
//这里通过RecyclerView的scrollBy方法实现滚动。
mRecyclerView.scrollBy(0, dy);
} else {
int dx = nowPoint - offsetX;
mRecyclerView.scrollBy(dx, 0);
}
}
});
2.5 翻页至某一页
这里翻页至某一页的实现有了上面的基础就很好实现了,就是直接调用 我们已经实现好了的 onFling(int velocityX, int velocityY) 方法,然后把页数传递过去计算一下就可以了 :
public void setPageNum(int page) {
this.pageNum = page;
mOnFlingListener.onFling(0, 0);
}
到这里前面说的功能已经全部实现完毕.
具体代码细节请见:
github 地址: pagerecyclerview
本地下载:点击这里
总结
以上就是这篇文章的全部内容了,希望本文的内容对各位Android开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
# recyclerview分页滚动
# recyclerview
# 分页
# recyclerview分页加载
# Android中实现多行、水平滚动的分页的Gridview实例源码
# Android之ListView分页加载数据功能实现代码
# Android实现ListView分页自动加载数据的方法
# android实现listview分页的方法
# Android Recyclerview实现水平分页GridView效果示例
# Android实现简单的分页效果
# Android提高之SQLite分页表格实现方法
# Android开发中滑动分页功能实例详解
# Android端代码量非常小的分页加载库
# 翻页
# 按下
# 当我们
# 第一个
# 好了
# 这两个
# 这两种
# 就可以
# 看着
# 直接调用
# 很好
# 看了
# 本地下载
# 在这个
# 下一页
# 不需要
# 就不
# 两种
# 中有
# 我们可以
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】
Laravel如何使用.env文件管理环境变量?(最佳实践)
Laravel怎么实现模型属性的自动加密
如何在浏览器中启用Flash_2025年继续使用Flash Player的方法【过时】
Laravel如何实现本地化和多语言支持?(i18n教程)
Laravel中DTO是什么概念_在Laravel项目中使用数据传输对象(DTO)
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
Laravel怎么上传文件_Laravel图片上传及存储配置
Android仿QQ列表左滑删除操作
laravel怎么用DB facade执行原生SQL查询_laravel DB facade原生SQL执行方法
Laravel怎么使用Markdown渲染文档_Laravel将Markdown内容转HTML页面展示【实战】
如何在云虚拟主机上快速搭建个人网站?
如何自定义建站之星模板颜色并下载新样式?
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
MySQL查询结果复制到新表的方法(更新、插入)
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
香港服务器如何优化才能显著提升网站加载速度?
如何获取上海专业网站定制建站电话?
在线教育网站制作平台,山西立德教育官网?
新三国志曹操传主线渭水交兵攻略
Laravel怎么连接多个数据库_Laravel多数据库连接配置
香港服务器网站卡顿?如何解决网络延迟与负载问题?
高配服务器限时抢购:企业级配置与回收服务一站式优惠方案
打开php文件提示内存不足_怎么调整php内存限制【解决方案】
如何选择可靠的免备案建站服务器?
如何为不同团队 ID 动态生成多个非值班状态按钮
邀请函制作网站有哪些,有没有做年会邀请函的网站啊?在线制作,模板很多的那种?
微信小程序制作网站有哪些,微信小程序需要做网站吗?
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
千库网官网入口推荐 千库网设计创意平台入口
JavaScript如何实现继承_有哪些常用方法
Laravel如何生成API文档?(Swagger/OpenAPI教程)
如何登录建站主机?访问步骤全解析
Swift中循环语句中的转移语句 break 和 continue
Edge浏览器提示“由你的组织管理”怎么解决_去除浏览器托管提示【修复】
Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】
javascript基于原型链的继承及call和apply函数用法分析
如何在企业微信快速生成手机电脑官网?
如何在香港免费服务器上快速搭建网站?
Laravel怎么使用Blade模板引擎_Laravel模板继承与Component组件复用【手册】
Laravel Eloquent关联是什么_Laravel模型一对一与一对多关系精讲
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
如何快速生成凡客建站的专业级图册?
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
C++用Dijkstra(迪杰斯特拉)算法求最短路径
深入理解Android中的xmlns:tools属性
php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】
如何快速查询网址的建站时间与历史轨迹?
QQ浏览器网页版登录入口 个人中心在线进入

