【发布时间】:2016-01-31 23:28:34
【问题描述】:
我需要在回收站视图中检测滚动的开始/结束和方向。滚动监听器有两种方法:onScrolled() 和onScrollStateChanged()。第一个方法是在滚动开始后调用的(实际上是调用 onScrolled() 而不是 onScrolling())。第二种方法提供有关状态的信息,但我没有方向信息。我怎样才能实现我的目标?
【问题讨论】:
标签: android android-recyclerview
我需要在回收站视图中检测滚动的开始/结束和方向。滚动监听器有两种方法:onScrolled() 和onScrollStateChanged()。第一个方法是在滚动开始后调用的(实际上是调用 onScrolled() 而不是 onScrolling())。第二种方法提供有关状态的信息,但我没有方向信息。我怎样才能实现我的目标?
【问题讨论】:
标签: android android-recyclerview
请参阅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;
}
【讨论】:
onScrolled(int dx, int dy) 并检查dy 是正数还是负数,从而为您提供一个或另一个方向。
第 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 最好使用 addOnScrollListener
mRecyclerView.addOnScrollListener(new CustomScrollListener());
【讨论】:
RecyclerView.OnScrollListener 不是一个界面 /facepalm @ 谷歌开发者决定这样做。
最简单的方法是拉出你的 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");
}
}
}
希望对您有所帮助!
【讨论】:
这是在滚动停止后执行操作的完整解决方案(用于 RecyclerView)
这已经纠正了我与 recyclerView 滚动相关的异常 OutOfBounds
recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
//Your action here
}
});
【讨论】:
我认为其他答案并没有触及你的痛点。
关于问题
如您所说,RecycleView 将在 onScrolled() 方法之前调用 onScrollStateChanged()。但是,onScrollStateChanged() 方法只能告诉你 RecycleView 的三种状态:
也就是说,假设 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
}
}
}
【讨论】:
使用此代码避免重复调用
使用 -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");
}
}
});
【讨论】:
@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
}
}
【讨论】:
您可以使用RecyclerView.getScrollState() 来检测RecyclerView.SCROLL_STATE_IDLE 或另一种状态。
我的情况:
if (recyclerView?.scrollState != RecyclerView.SCROLL_STATE_IDLE) {
return
}
【讨论】: