我需要检测循环视图中滚动的开始/结束和方向。滚动监听器有两种方法:onScrolled()
和onScrollStateChanged()
。第一个方法在滚动开始后调用(实际上被称为onScrolled(),而不是onScrolling())。第二种方法提供有关状态的信息,但我没有方向信息。我该如何实现我的目标?
步骤1您可以创建一个扩展RecyclerView.OnScrollListener的类并覆盖这些方法
public class CustomScrollListener extends RecyclerView.OnScrollListener {
public CustomScrollListener() {
}
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
System.out.println("The RecyclerView is not scrolling");
break;
case RecyclerView.SCROLL_STATE_DRAGGING:
System.out.println("Scrolling now");
break;
case RecyclerView.SCROLL_STATE_SETTLING:
System.out.println("Scroll Settling");
break;
}
}
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (dx > 0) {
System.out.println("Scrolled Right");
} else if (dx < 0) {
System.out.println("Scrolled Left");
} else {
System.out.println("No Horizontal Scrolled");
}
if (dy > 0) {
System.out.println("Scrolled Downwards");
} else if (dy < 0) {
System.out.println("Scrolled Upwards");
} else {
System.out.println("No Vertical Scrolled");
}
}
}
步骤2-由于setOnScrollListener已弃用,因此最好使用addOnScrollLister
mRecyclerView.addOnScrollListener(new CustomScrollListener());
请参阅onScrollStateChanged(int state)
的文档。三个可能的值是:
SCROLL_STATE_IDLE
:不进行滚动SCROLL_STATE_DRAGGING
:用户正在屏幕上拖动他的手指(或者这是通过程序进行的)SCROLL_STATE_SETTLING
:用户已经举起手指,动画现在正在减速
因此,如果你想检测滚动的开始和结束时间,那么你可以创建这样的东西:
public void onScrollStateChanged(int state) {
boolean hasStarted = state == SCROLL_STATE_DRAGGING;
boolean hasEnded = state == SCROLL_STATE_IDLE;
}
使用此代码来避免重复调用
使用-1检测顶部
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(1) && newState==RecyclerView.SCROLL_STATE_IDLE) {
Log.d("-----","end");
}
}
});
最简单的方法是退出layoutManager。例如
private RecyclerView.OnScrollListener mListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
GridLayoutManager layoutManager=GridLayoutManager.class.cast(recyclerView.getLayoutManager());
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
int pastVisibleItems = layoutManager.findFirstCompletelyVisibleItemPosition();
if(pastVisibleItems+visibleItemCount >= totalItemCount){
// End of the list is here.
Log.i(TAG, "End of list");
}
}
}
希望它能有所帮助!
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
//reached end
}
if (!recyclerView.canScrollVertically(-1) && newState == RecyclerView.SCROLL_STATE_IDLE) {
//reached top
}
if(newState == RecyclerView.SCROLL_STATE_DRAGGING){
//scrolling
}
}
我认为其他答案没有触及你的痛点。
关于问题
正如您所说,RecycleView将在onScrolled()方法之前调用onScrollStateChanged()。然而,onScrollStateChanged()方法只能告诉您RecycleView的三种状态:
- 滚动状态IDLE
- SCROLL_STATE_DRAGGING
- 滚动状态设置
也就是说,假设RecycleView现在处于顶部,如果用户向上滚动,它只能触发onScrollStateChanged()
方法,而不能触发onScrolled()
方法。如果用户向下滚动,可以先触发onScrollStateChanged()
方法,然后再触发onScrolled()
方法。
那么问题来了,SCROLL_STATE_DRAGGING
只能告诉你用户正在拖动视图,而不能告诉他拖动的方向。
您必须与onScrolled()
方法中的dy
结合才能检测拖动方向,但onScrolled()
未与onScrollStateChanged()
触发。
我的解决方案:
记录onScrollStateChanged()
触发时的滚动状态,然后删除一个时间,如10ms,检查是否有Y轴移动,并得出拖动方向的结论。
Kotlin代码:
class DraggingDirectionScrollListener(private val view: View)
: RecyclerView.OnScrollListener() {
private val DIRECTION_NONE = -1
private val DIRECTION_UP = 0
private val DIRECTION_DOWN = 1
var scrollDirection = DIRECTION_NONE
var listStatus = RecyclerView.SCROLL_STATE_IDLE
var totalDy = 0
override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
listStatus = newState
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
scrollDirection = DIRECTION_NONE
}
if (isOnTop() && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
view.postDelayed({
if (getDragDirection() == DIRECTION_DOWN){
// Drag down from top
}else if (getDragDirection() == DIRECTION_UP) {
// Drag Up from top
}
}, 10)
}
}
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
this.totalDy += dy
scrollDirection = when {
dy > 0 -> DIRECTION_UP
dy < 0 -> DIRECTION_DOWN
else -> DIRECTION_NONE
}
}
/**
* is on the top of the list
*/
private fun isOnTop():Boolean{
return totalDy == 0
}
/**
* detect dragging direction
*/
private fun getDragDirection():Int {
if (listStatus != RecyclerView.SCROLL_STATE_DRAGGING) {
return DIRECTION_NONE
}
return when (scrollDirection) {
DIRECTION_NONE -> if (totalDy == 0){
DIRECTION_DOWN // drag down from top
}else{
DIRECTION_UP // drag up from bottom
}
DIRECTION_UP -> DIRECTION_UP
DIRECTION_DOWN -> DIRECTION_DOWN
else -> DIRECTION_NONE
}
}
}
您可以使用RecyclerView.getScrollState()
来检测RecyclerView.SCROLL_STATE_IDLE
或其他状态。
我的案例:
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_IDLE) {
return
}
其他不适合我的答案,它们要么做了其他事情,要么工作不完美。这适用于
binding.filters.addOnScrollListener(object : OnScrollListener() {
private var layoutManager: LinearLayoutManager? = null
var totalItemCount = 0
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (layoutManager == null) {
layoutManager = recyclerView.layoutManager as LinearLayoutManager
totalItemCount = layoutManager?.itemCount ?: 0
}
if(layoutManager?.findFirstCompletelyVisibleItemPosition() == 0) //reached start
if(layoutManager?.findLastCompletelyVisibleItemPosition() == totalItemCount - 1) //reached end
}
})
这里有一个完整的解决方案,可以在滚动停止后进行操作(对于RecyclerView)
已经更正了我与回收器相关的异常越界查看滚动
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//Your action here
}
});