【问题标题】:RecyclerView scrolled UP/DOWN listenerRecyclerView 向上/向下滚动监听器
【发布时间】:2015-05-15 10:51:44
【问题描述】:

我们如何知道用户在RecyclerView 中是向下滚动还是向上滚动?

我尝试使用 RecyclerView#OnScrollListener ,它给出了垂直滚动量和滚动状态。滚动状态空闲时如何获取开始拖动和滚动位置时的最后滚动位置。

【问题讨论】:

标签: java android android-recyclerview onscrolllistener


【解决方案1】:

这段代码对我有用

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)
                    }

        }
    }
    ```

【讨论】:

    【解决方案2】:

    我的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());
        }
    };
    

    【讨论】:

      【解决方案3】:

      另一个可以借助适配器检测滚动方向的简单解决方案:

      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。

      【讨论】:

        【解决方案4】:

        接受的答案工作正常,但@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
                }
            }
        });
        

        【讨论】:

        • 你可以使用setOnScrollListener(...)而不是addOnScrollListener(...)
        • 考虑使用switch (newState) {...}构造
        • AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL 值和recyclerView 值一样吗?
        • 它工作正常,但是当我尝试缓慢滚动时,滚动条会晃动,即缩小滚动条如何解决?
        • 最好不要使用条件 (dy > 0) 因为如果 dy == 0 这意味着它没有滚动,但在您的代码中它将被处理为“向下滚动”。我建议使用两个条件(dy > 0)和(dy
        【解决方案5】:

        我想在 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);
                            }
                        }
                    }
                });
        

        【讨论】:

        • 那么 EndlessRecyclerOnScrollListener 类的内容是什么?
        • @AhmetCanAkgül 内容是一个适配器,其中包含图像视图、文本视图列表。
        • 工作,谢谢
        【解决方案6】:

        试试这个方法:

        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;
        
        }
        

        【讨论】:

        • 谢谢。有效。但我在onScrollStateChanged `RecyclerView.SCROLL_STATE_DRAGGING' 状态下处理这个逻辑。
        • if (dy &gt; 0) { // scrolling up } else { // scrolling down } 还不够吗?
        • 我放在 scrollup 中的方法永远不会被调用...但在 logcat 中它会上下显示
        • @Maciej Pigulski dx 和 dy 的问题在于,如果滚动缓慢,则 dx 和 dy 值不可靠。对于大多数应用程序来说应该没问题,但如果你想要精确,答案中给出的方式会更好。
        • 每次向下滚动都会被调用。为什么?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-18
        • 1970-01-01
        相关资源
        最近更新 更多