【问题标题】:RecyclerView scrollToPosition not trigger scrollListenerRecyclerView scrollToPosition 不触发 scrollListener
【发布时间】:2015-03-05 08:24:54
【问题描述】:

我正在使用带有 ScrollListener 的 RecyclerView:

mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener()
{
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState)
        {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy)
        {
            super.onScrolled(recyclerView, dx, dy);
            // Do my logic
        }
 });

当我用手指滚动时,滚动监听器触发良好。

但是当我逐步滚动时,就像这样:

mRecyclerView.scrollToPosition(LAST_POSITION);

滚动监听器没有被触发。

【问题讨论】:

  • 在内部它将调用转发给布局管理器。直接尝试使用布局管理器滚动。是否滚动到预期位置?
  • 是的,mRecyclerView.scrollToPosition(LAST_POSITION)滚动到预期的位置,使用布局管理器是什么意思? mRecyclerView.getLayoutManager().scrollToPosition(LAST_POSITION) 仍然没有触发 ScrollListener

标签: android android-recyclerview onscrolllistener


【解决方案1】:

我遇到了同样的问题并找到了解决方法,即在布局管理器中使用 smoothScrollToPosition。此方法触发滚动侦听器。所以基本上这就是我曾经拥有的:

recyclerView.scrollToPosition(recyclerAdapter.getItemCount() - 1);

现在我改成了这样:

recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null, recyclerAdapter.getItemCount() - 1);

它对我来说很好用。

【讨论】:

  • 您是否按照文档中的说明覆盖了该方法,或者您只是按原样使用它?
  • 不想流畅滚动怎么办?
  • 那么没有动画
【解决方案2】:

这是一个已知问题。这是因为 RecyclerView 不知道 LayoutManager 将如何处理滚动或者它是否会处理它。

在下一个版本中,如果第一个或最后一个子位置在布局后发生变化(这通常是调用滚动到位置的结果),您将收到对 onScrolled 的调用。

不幸的是,dx 和 dy 将为 0,因为 RecyclerView 并不真正知道布局管理器滚动了多少来处理 scrollTo 请求。或者,您也可以使用 ViewTreeObserver 的滚动回调。

【讨论】:

  • :( 谢谢,你能给上面这个案例的例子吗?或者链接使用 ViewTreeObserver 的滚动回调?
  • 对不起,门口机的滚动回调在下一个版本之前也不会起作用。 :/。如果您需要紧急修复,您的选择是覆盖 RV,将侦听器保留在您可以访问的列表中,然后在onLayout 呼叫后通知他们第一个或最后一个子位置是否发生变化。
  • @yigit 对此有何更新?我有同样的问题,一直在寻找解决方案,但没有找到。
  • @David 这个问题通常是因为Recyclerview 的滚动速度非常快。我建议使用 handler 和 1 秒 delay 来实现滚动。 whatsApp 似乎就是这样做的。
  • 只是想插话并提到我也遇到了这个问题,我认为这是由于在 Choreographer 线程或其他一些非 ui 线程上运行的滚动代码并从它。虽然它没有显式失败,但它确实导致视图不在正确的位置重绘。如果你调用 recyclerView.post(() -> recyclerView.scrollToPosition(position));它应该可以立即工作。
【解决方案3】:

要通过 recycler 视图显式触发 onScrollListener:

recyclerView.smoothScrollBy(x, y);
//x is the horizontal displacement and y is vertical displacement.

【讨论】:

    【解决方案4】:

    有点简陋,但你可以试试这个 RecyclerView 的子类:

    public class PatchedRecyclerView extends RecyclerView {
    
        private OnScrollListener listener;
    
        public PatchedRecyclerView(Context context) {
            super(context);
        }
    
        public PatchedRecyclerView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public PatchedRecyclerView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        @Override
        public void setOnScrollListener(OnScrollListener listener) {
            super.setOnScrollListener(listener);
            this.listener = listener;
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
    
            int preFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
            int preLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
            super.onLayout(changed, l, t, r, b);
            int postFirstChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(0)).getPosition() : -1;
            int postLastChildPosition = getChildCount() != 0 ? getChildViewHolder(getChildAt(getChildCount() - 1)).getPosition() : -1;
    
            // TODO: Insert proper DX and DY values
            if (preFirstChildPosition != postFirstChildPosition || preLastChildPosition != postLastChildPosition)
                listener.onScrolled(this, 0, 0);
        }
    }
    

    【讨论】:

      【解决方案5】:

      Muito 法律 seu conselho。

       Button maisOpcoes = (Button) findViewById(R.id.maisOpcoes);
              maisOpcoes.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, null , recyclerViewTmd.getItemCount() - 1);
                      maisOpcoes.setVisibility(View.GONE);
                  }
              });
      

      【讨论】:

      • 请使用英文
      【解决方案6】:
      int mFirst=0, mLast=0;
      
      recyclerview.setOnScrollListener(new RecyclerView.OnScrollListener() {
          @Override
          public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
              super.onScrollStateChanged(recyclerView, newState);
          }
          @Override
          public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);
              LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
              mLast = llm.findLastCompletelyVisibleItemPosition();
              mFirst = llm.findFirstCompletelyVisibleItemPosition();
          }
      });
      
      imgRight.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
              llm.scrollToPositionWithOffset(mLast + 1, List.length());
          }
      });
      
      imgLeft.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              LinearLayoutManager llm = (LinearLayoutManager) recyclerview.getLayoutManager();
              llm.scrollToPositionWithOffset(mFirst - 1, List.length());
          }
      });
      

      【讨论】:

        【解决方案7】:

        我无法使用smoothScrollToPosition(过去我们遇到过一些问题)并且根据第一项的位置似乎不可靠,所以我最终使用了yigit的答案(它已经发布了) ,不幸的是它并不总是有效(我不知道为什么)。

        所以我最终使用了我之前添加到 RecyclerView 的滚动监听器,遗憾的是我找不到这个解决方案的来源。
        如果你需要一个恒定的监听器,我认为你不应该使用这个监听器,幸运的是我只需要在特定时间收听。

        覆盖 RecyclerView:

        public class MyRecyclerView extends RecyclerView {
        
            public interface OnScrollStoppedListener{
                void onScrollStopped();
            }
        
            private static final int NEW_CHECK_INTERVAL = 100;
            private OnScrollStoppedListener mOnScrollStoppedListener;
            private Runnable mScrollerTask;
            private int mInitialPosition;
        
            public MyRecyclerView(Context context) {
                super(context);
                init();
            }
        
            public MyRecyclerView(Context context, @Nullable AttributeSet attrs) {
                super(context, attrs);
                init();
            }
        
            public MyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
                init();
            }
        
            private void init() {
                mScrollerTask = new Runnable() {
                    public void run() {
                        int newPosition = getScrollY();
                        if(mInitialPosition - newPosition == 0) {//has stopped
                            if(mOnScrollStoppedListener !=null) {
                                mOnScrollStoppedListener.onScrollStopped();
                            }
                        } else {
                            startScrollerTask();
                        }
                    }
                };
            }
        
            public void setOnScrollStoppedListener(MyRecyclerView.OnScrollStoppedListener listener){
                mOnScrollStoppedListener = listener;
            }
        
            public void startScrollerTask() {
                mInitialPosition = getScrollY();
                postDelayed(mScrollerTask, NEW_CHECK_INTERVAL);
            }
        
        }  
        

        用法:

        myRecyclerView.setOnScrollStoppedListener(new MyRecyclerView.OnScrollStoppedListener() {
            @Override
            public void onScrollStopped() {
                // Do your thing
            }
        });
        
        myRecyclerView.startScrollerTask();
        

        【讨论】:

          【解决方案8】:

          请尝试

          LinearLayoutManager.scrollToPositionWithOffset(int, int).
          

          LayoutManager.scrollToPosition() 可能无法正常工作,但LinearLayoutManager.scrollToPositionWithOffset() 可以。 GridLayoutManager 也是如此。

          或者我们必须编写自定义的实现方式,因为 RecyclerView 不知道 LayoutManager 是如何滚动视图的。

              class CustomLayoutManager extends LinearLayoutManager{
                   public void smoothScrollToPosition(RecyclerView recyclerView,
                          RecyclerView.State state, final int position) {
          
                    LinearSmoothScroller smoothScroller = 
                              new LinearSmoothScroller(mContext) {
                  //Override the methods and write your implementation as per your requirement 
                 //one which controls the direction and another one calculate the speed per Pixel
                  }
              }
          

          有时候这样可以解决问题

              new Handler().postDelayed(new Runnable() {
              @Override
              public void run() {
                  yourList.scrollToPosition(position);
              }
          }, 200);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-02-15
            • 2017-05-28
            • 1970-01-01
            • 1970-01-01
            • 2015-08-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多