RecyclerView 滚动 UP/DOWN 侦听器
RecyclerView scrolled UP/DOWN listener
我们如何知道用户在 RecyclerView
中向下滚动还是向上滚动?
我试过 RecyclerView#OnScrollListener
,它给出了垂直滚动量和滚动状态。如何获取开始拖动时的最后滚动位置和滚动状态空闲时的滚动位置。
试试这个方法:
private static int firstVisibleInListview;
firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();
在你的滚动监听器中:
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
int currentFirstVisible = yourLayoutManager.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview)
Log.i("RecyclerView scrolled: ", "scroll up!");
else
Log.i("RecyclerView scrolled: ", "scroll down!");
firstVisibleInListview = currentFirstVisible;
}
接受的答案工作正常,但@MaciejPigulski 在下面的评论中给出了更清晰、更简洁的解决方案。我只是把它作为答案放在这里。这是我的工作代码。
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
// Scrolling up
} else {
// Scrolling down
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
// Do something
} else {
// Do something
}
}
});
我想在 recyclerview 向下滚动时隐藏布局,然后在 recyclerview 向上滚动时使其可见。我做了一些思考并提出了这个逻辑。
变量 y 是一个全局静态 int。不要忘记将 y 声明为 static int y;
希望对大家有所帮助:)
mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(lLayout) {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// super.onScrolled(recyclerView, dx, dy);
y=dy;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(mRecyclerView.SCROLL_STATE_DRAGGING==newState){
//fragProductLl.setVisibility(View.GONE);
}
if(mRecyclerView.SCROLL_STATE_IDLE==newState){
// fragProductLl.setVisibility(View.VISIBLE);
if(y<=0){
fragProductLl.setVisibility(View.VISIBLE);
}
else{
y=0;
fragProductLl.setVisibility(View.GONE);
}
}
}
});
另一个可以在适配器的帮助下检测滚动方向的简单解决方案:
class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyViewHolder> {
int lastItemPosition = -1;
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if (position > lastItemPosition) {
// Scrolled Down
}
else {
// Scrolled Up
}
lastItemPosition = position;
}
}
^ 在滚动时制作项目动画时很有用。
PS:这将不连续地告诉您方向,直到进一步调用 onBindViewHolder。
我的 CustomRecyclerView
实现了所有类型的滚动侦听器
public class CustomRecyclerView extends RecyclerView
{
private boolean mIsScrolling;
private boolean mIsTouching;
private OnScrollListener mOnScrollListener;
private Runnable mScrollingRunnable;
private int y = 0;
public abstract static class OnScrollListener
{
void onScrollChanged(CustomRecyclerView RvView, int x, int y, int oldX, int oldY)
{
//if you need just override this method
}
void onEndScroll(CustomRecyclerView RvView)
{
//if you need just override this method
}
protected abstract void onGoUp();
protected abstract void onGoDown();
}
public CustomRecyclerView(final Context context)
{
super(context);
}
public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs)
{
super(context, attrs);
}
public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
@Override
public boolean dispatchTouchEvent(MotionEvent iEv)
{
if (isEnabled())
{
processEvent(iEv);
super.dispatchTouchEvent(iEv);
return true; //to keep receive event that follow down event
}
return super.dispatchTouchEvent(iEv);
}
private void processEvent(final MotionEvent iEv)
{
switch (iEv.getAction())
{
case MotionEvent.ACTION_DOWN:
y = (int) iEv.getY();
break;
case MotionEvent.ACTION_UP:
y = (int) iEv.getY();
if (mIsTouching && !mIsScrolling && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(this);
}
mIsTouching = false;
break;
case MotionEvent.ACTION_MOVE:
mIsTouching = true;
mIsScrolling = true;
int newY = (int) iEv.getY();
int difY = y - newY;
int MAX_VALUE = 200;
int MIN_VALUE = -200;
if (difY > MAX_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoDown();
}
y = newY;
}
else if (difY < MIN_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoUp();
}
y = newY;
}
break;
}
}
@Override
protected void onScrollChanged(int iX, int iY, int iOldX, int iOldY)
{
super.onScrollChanged(iX, iY, iOldX, iOldY);
if (Math.abs(iOldX - iX) > 0)
{
if (mScrollingRunnable != null)
{
removeCallbacks(mScrollingRunnable);
}
mScrollingRunnable = () ->
{
if (mIsScrolling && !mIsTouching && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(CustomRecyclerView.this);
}
mIsScrolling = false;
mScrollingRunnable = null;
};
postDelayed(mScrollingRunnable, 200);
}
if (mOnScrollListener != null)
{
mOnScrollListener.onScrollChanged(this, iX, iY, iOldX, iOldY);
}
}
public void scrollToView(final View iV)
{
// Get deepChild Offset
Point childOffset = new Point();
getDeepChildOffset(CustomRecyclerView.this, iV.getParent(), iV, childOffset);
// Scroll to child.
CustomRecyclerView.this.scrollToY(childOffset.y);
}
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset)
{
ViewGroup parentGroup = (ViewGroup) parent;
accumulatedOffset.x += child.getLeft();
accumulatedOffset.y += child.getTop();
if (parentGroup.equals(mainParent))
{
return;
}
getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}
public void scrollToY(final int iY)
{
CustomRecyclerView.this.postDelayed(() ->
{
int x = 0;
int y = iY;
ObjectAnimator xTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollX", x);
ObjectAnimator yTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollY", y);
AnimatorSet animators = new AnimatorSet();
animators.setDuration(500L);
animators.playTogether(xTranslate, yTranslate);
animators.addListener(new Animator.AnimatorListener()
{
@Override
public void onAnimationStart(Animator arg0)
{
// noting
}
@Override
public void onAnimationRepeat(Animator arg0)
{
// noting
}
@Override
public void onAnimationEnd(Animator arg0)
{
// noting
}
@Override
public void onAnimationCancel(Animator arg0)
{
// noting
}
});
animators.start();
}, 300);
}
public void scrollToTop()
{
scrollToY(0);
}
public void setOnRvScrollListener(OnScrollListener mOnEndScrollListener)
{
this.mOnScrollListener = mOnEndScrollListener;
}
}
并且有使用示例
private void setRecyclerViewSettings()
{
mRv.setLayoutManager(new LinearLayoutManager(getActivity()));
mRv.setOnRvScrollListener(mScrollListener);
}
private CustomRecyclerView.OnScrollListener mScrollListener = new CustomRecyclerView.OnScrollListener()
{
@Override
protected void onGoUp()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
@Override
protected void onGoDown()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
};
这段代码对我有用
private var nearmeListScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_SETTLING)
if (dy > 0) {
layout_vendros_list_header.hide()
layout_vendros_list_header.animate().translationY(1f)
} else if (dy < 0) {
layout_vendros_list_header.show()
layout_vendros_list_header.animate().translationY(0f)
}
}
}
```
我们如何知道用户在 RecyclerView
中向下滚动还是向上滚动?
我试过 RecyclerView#OnScrollListener
,它给出了垂直滚动量和滚动状态。如何获取开始拖动时的最后滚动位置和滚动状态空闲时的滚动位置。
试试这个方法:
private static int firstVisibleInListview;
firstVisibleInListview = yourLayoutManager.findFirstVisibleItemPosition();
在你的滚动监听器中:
public void onScrolled(RecyclerView recyclerView, int dx, int dy)
{
super.onScrolled(recyclerView, dx, dy);
int currentFirstVisible = yourLayoutManager.findFirstVisibleItemPosition();
if(currentFirstVisible > firstVisibleInListview)
Log.i("RecyclerView scrolled: ", "scroll up!");
else
Log.i("RecyclerView scrolled: ", "scroll down!");
firstVisibleInListview = currentFirstVisible;
}
接受的答案工作正常,但@MaciejPigulski 在下面的评论中给出了更清晰、更简洁的解决方案。我只是把它作为答案放在这里。这是我的工作代码。
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
// Scrolling up
} else {
// Scrolling down
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
// Do something
} else if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
// Do something
} else {
// Do something
}
}
});
我想在 recyclerview 向下滚动时隐藏布局,然后在 recyclerview 向上滚动时使其可见。我做了一些思考并提出了这个逻辑。
变量 y 是一个全局静态 int。不要忘记将 y 声明为 static int y;
希望对大家有所帮助:)
mRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(lLayout) {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
// super.onScrolled(recyclerView, dx, dy);
y=dy;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if(mRecyclerView.SCROLL_STATE_DRAGGING==newState){
//fragProductLl.setVisibility(View.GONE);
}
if(mRecyclerView.SCROLL_STATE_IDLE==newState){
// fragProductLl.setVisibility(View.VISIBLE);
if(y<=0){
fragProductLl.setVisibility(View.VISIBLE);
}
else{
y=0;
fragProductLl.setVisibility(View.GONE);
}
}
}
});
另一个可以在适配器的帮助下检测滚动方向的简单解决方案:
class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyViewHolder> {
int lastItemPosition = -1;
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
if (position > lastItemPosition) {
// Scrolled Down
}
else {
// Scrolled Up
}
lastItemPosition = position;
}
}
^ 在滚动时制作项目动画时很有用。
PS:这将不连续地告诉您方向,直到进一步调用 onBindViewHolder。
我的 CustomRecyclerView
实现了所有类型的滚动侦听器
public class CustomRecyclerView extends RecyclerView
{
private boolean mIsScrolling;
private boolean mIsTouching;
private OnScrollListener mOnScrollListener;
private Runnable mScrollingRunnable;
private int y = 0;
public abstract static class OnScrollListener
{
void onScrollChanged(CustomRecyclerView RvView, int x, int y, int oldX, int oldY)
{
//if you need just override this method
}
void onEndScroll(CustomRecyclerView RvView)
{
//if you need just override this method
}
protected abstract void onGoUp();
protected abstract void onGoDown();
}
public CustomRecyclerView(final Context context)
{
super(context);
}
public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs)
{
super(context, attrs);
}
public CustomRecyclerView(final Context context, @Nullable final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
}
@Override
public boolean dispatchTouchEvent(MotionEvent iEv)
{
if (isEnabled())
{
processEvent(iEv);
super.dispatchTouchEvent(iEv);
return true; //to keep receive event that follow down event
}
return super.dispatchTouchEvent(iEv);
}
private void processEvent(final MotionEvent iEv)
{
switch (iEv.getAction())
{
case MotionEvent.ACTION_DOWN:
y = (int) iEv.getY();
break;
case MotionEvent.ACTION_UP:
y = (int) iEv.getY();
if (mIsTouching && !mIsScrolling && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(this);
}
mIsTouching = false;
break;
case MotionEvent.ACTION_MOVE:
mIsTouching = true;
mIsScrolling = true;
int newY = (int) iEv.getY();
int difY = y - newY;
int MAX_VALUE = 200;
int MIN_VALUE = -200;
if (difY > MAX_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoDown();
}
y = newY;
}
else if (difY < MIN_VALUE)
{
if (mOnScrollListener != null)
{
mOnScrollListener.onGoUp();
}
y = newY;
}
break;
}
}
@Override
protected void onScrollChanged(int iX, int iY, int iOldX, int iOldY)
{
super.onScrollChanged(iX, iY, iOldX, iOldY);
if (Math.abs(iOldX - iX) > 0)
{
if (mScrollingRunnable != null)
{
removeCallbacks(mScrollingRunnable);
}
mScrollingRunnable = () ->
{
if (mIsScrolling && !mIsTouching && mOnScrollListener != null)
{
mOnScrollListener.onEndScroll(CustomRecyclerView.this);
}
mIsScrolling = false;
mScrollingRunnable = null;
};
postDelayed(mScrollingRunnable, 200);
}
if (mOnScrollListener != null)
{
mOnScrollListener.onScrollChanged(this, iX, iY, iOldX, iOldY);
}
}
public void scrollToView(final View iV)
{
// Get deepChild Offset
Point childOffset = new Point();
getDeepChildOffset(CustomRecyclerView.this, iV.getParent(), iV, childOffset);
// Scroll to child.
CustomRecyclerView.this.scrollToY(childOffset.y);
}
private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset)
{
ViewGroup parentGroup = (ViewGroup) parent;
accumulatedOffset.x += child.getLeft();
accumulatedOffset.y += child.getTop();
if (parentGroup.equals(mainParent))
{
return;
}
getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset);
}
public void scrollToY(final int iY)
{
CustomRecyclerView.this.postDelayed(() ->
{
int x = 0;
int y = iY;
ObjectAnimator xTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollX", x);
ObjectAnimator yTranslate = ObjectAnimator.ofInt(CustomRecyclerView.this, "scrollY", y);
AnimatorSet animators = new AnimatorSet();
animators.setDuration(500L);
animators.playTogether(xTranslate, yTranslate);
animators.addListener(new Animator.AnimatorListener()
{
@Override
public void onAnimationStart(Animator arg0)
{
// noting
}
@Override
public void onAnimationRepeat(Animator arg0)
{
// noting
}
@Override
public void onAnimationEnd(Animator arg0)
{
// noting
}
@Override
public void onAnimationCancel(Animator arg0)
{
// noting
}
});
animators.start();
}, 300);
}
public void scrollToTop()
{
scrollToY(0);
}
public void setOnRvScrollListener(OnScrollListener mOnEndScrollListener)
{
this.mOnScrollListener = mOnEndScrollListener;
}
}
并且有使用示例
private void setRecyclerViewSettings()
{
mRv.setLayoutManager(new LinearLayoutManager(getActivity()));
mRv.setOnRvScrollListener(mScrollListener);
}
private CustomRecyclerView.OnScrollListener mScrollListener = new CustomRecyclerView.OnScrollListener()
{
@Override
protected void onGoUp()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
@Override
protected void onGoDown()
{
AppUtils.hideKeyboard(getContext(), mRvDynamicFormQuestions.getFocusedChild());
}
};
这段代码对我有用
private var nearmeListScrollListener = object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (recyclerView.scrollState == RecyclerView.SCROLL_STATE_SETTLING)
if (dy > 0) {
layout_vendros_list_header.hide()
layout_vendros_list_header.animate().translationY(1f)
} else if (dy < 0) {
layout_vendros_list_header.show()
layout_vendros_list_header.animate().translationY(0f)
}
}
}
```