【问题标题】:How to scroll to the bottom of a RecyclerView? scrollToPosition doesn't work如何滚动到 RecyclerView 的底部?滚动到位置不起作用
【发布时间】:2014-12-22 05:28:15
【问题描述】:

我想在加载活动后滚动到 RecyclerView 列表的底部。

GENERIC_MESSAGE_LIST = (ArrayList) intent.getExtras().getParcelableArrayList(ConversationsAdapter.EXTRA_MESSAGE);
conversationView = (RecyclerView) findViewById(R.id.list_messages);
conversationView.setHasFixedSize(true);
conversationViewLayoutManager = new LinearLayoutManager(this);
conversationView.setLayoutManager(conversationViewLayoutManager);
conversationViewAdapter = new ConversationAdapter(GENERIC_MESSAGE_LIST, this);
conversationView.setAdapter(conversationViewAdapter);

conversationView.scrollTo(...) 抛出 RecyclerView 不支持的异常,conversationView.scrollToPosition(...) 似乎没有做任何事情。

在上面的代码块之后,我添加了

conversationView.scrollToPosition(GENERIC_MESSAGE_LIST.size() + 1)

这是行不通的。 GENERIC_MESSAGE_LIST中有30个元素。

【问题讨论】:

  • 您是在设置适配器后立即调用scrollToPosition 吗?
  • @ianhanniballake 是的,就在conversationView.setAdapter(conversationViewAdapter); 之后。
  • 这可能是因为您调用的是 +1 而不是 -1,所以第 5 个元素将是位置 [4],因为它从 0 开始。执行 +1 会给您一个 ArrayOutOfBounds ... 做到了不会在那里崩溃吗?
  • 在setadapter之后添加 mRecyclerView.scrollToPosition(carsList.size()-1);

标签: java android scroll android-recyclerview


【解决方案1】:

我一直在查看这篇文章以寻找答案,但是...我认为这篇文章中的每个人都面临着与我相同的情况:scrollToPosition() 被完全忽略了,原因很明显。

我用的是什么?

recyclerView.scrollToPosition(items.size());

...什么工作

recyclerView.scrollToPosition(items.size() - 1);

【讨论】:

  • recyclerView.scrollToPosition(messages.size()-1); 真的很适合我,我只是想知道原因。
  • 这对我有用。这实际上是有道理的,因为 Java 列表是从零开始的。例如。列表 [0,1,2] 的大小为 3,但最后一个位置为 2,因为它以 0 开头。
  • 仅供参考,为了平滑滚动,有smoothScrollToPosition(...)
  • @Ivan Morgilo smoothScrollToPosition 根本不起作用:S
  • @IvanMorgillo - 你的答案似乎更适合想要的结果和更流畅的=D。似乎滚动到位置对我来说有点麻烦(我的猜测是调用的函数和视图的创建之间有一个小的延迟?无论如何它不能正常工作)
【解决方案2】:

只需设置 setStackFromEnd=truesetReverseLayout=true,这样 LLM 就会从头开始布局项目。

这两者的区别在于setStackFromEnd会设置视图显示最后一个元素,布局方向保持不变。 (因此,在从左到右的水平 Recycler 视图中,将显示最后一个元素,向左滚动将显示较早的元素)

setReverseLayout 将改变适配器添加的元素的顺序。布局将从最后一个元素开始,这将是 LTR Recycler 视图中最左侧的元素,然后向右滚动将显示较早的元素。

示例:

final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setReverseLayout(true);
_listView.setLayoutManager(linearLayoutManager);

详情请见documentation

【讨论】:

  • 这可能很有用,但似乎无法回答所提出的问题。
  • 如果你同时设置了 stackFromEnd=true 和 setReverseLayout=true 它就不起作用。有谁知道任何解决方法?
  • 对于聊天视图 linearLayoutManager.setStackFromEnd(true) 有效。
  • 这根本没有解决问题。
  • 仅在视图已满之前有效。它不打算滚动,只是从底部堆叠。
【解决方案3】:

我知道这里回答太晚了,如果有人想知道解决方案如下

conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() - 1);

【讨论】:

  • 它应该是conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);,因为列表中的位置是从零开始的。
  • 如果最后一行的高度大于屏幕高度,这将无济于事。
  • 这是我需要的。尽管我必须将它包装在处理程序 postdelay 中以使一切正常工作。
【解决方案4】:

从recyclerview的任意位置向下滚动到底部

科特林

editText.setOnClickListener {
    rv.postDelayed({                 
        rv.scrollToPosition(rv.adapter.itemCount - 1)
    }, 1000)
}

Java

edittext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                rv.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                      rv.scrollToPosition(rv.getAdapter().getItemCount() - 1);
                    }
                }, 1000);
            }
        });

【讨论】:

    【解决方案5】:

    在发送消息后和从服务器获取消息之前添加此代码

    recyclerView.scrollToPosition(mChatList.size() - 1);
    

    【讨论】:

      【解决方案6】:

      当您调用 setAdapter 时,它不会立即在屏幕上布局和定位项目(需要一次布局传递),因此您的 scrollToPosition() 调用在您调用它时没有可滚动到的实际元素。

      相反,您应该注册一个 ViewTreeObserver.OnGlobalLayoutListener(通过 ViewTreeObserver 创建的 addOnGlobalLayoutListner() 中的 conversationView.getViewTreeObserver()),这会将您的 scrollToPosition() 延迟到第一次布局传递之后:

      conversationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        public void onGlobalLayout() {
          conversationView.scrollToPosition(GENERIC_MESSAGE_LIST.size();
          // Unregister the listener to only call scrollToPosition once
          conversationView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      
          // Use vto.removeOnGlobalLayoutListener(this) on API16+ devices as 
          // removeGlobalOnLayoutListener is deprecated.
          // They do the same thing, just a rename so your choice.
        }
      });
      

      【讨论】:

      • 谢谢,但这不起作用。它通过停止工作来中断滚动,如果你拖动它,它会以奇怪的速度滚动。
      • 听起来你并没有删除监听器。它应该只被调用一次(在元素第一次布局之后),而不是在滚动期间。
      • OnGlobalLayoutListener 可能不会被删除,因为您正在检查 vto.isAlive()。我不知道原因,但ViewTreeObserver 有时会更改为View,所以与其检查isAlive,不如用conversationView.getViewTreeObserver().removeGlobalOnLayoutListener 获取当前的ViewTreeObserver
      • @ianhanniballake 我已提议修改以解决此问题。
      【解决方案7】:

      Kotlin 的解决方案:

      在设置“recyclerView.adapter”或“recyclerView.adapter.notifyDataSetChanged()”之后应用下面的代码

      recyclerView.scrollToPosition(recyclerView.adapter.itemCount - 1)
      

      【讨论】:

        【解决方案8】:

        如果你有适配器与 recyclerView 连接,那么就这样做。

        mRecyclerView.smoothScrollToPosition(mRecyclerView.getAdapter().getItemCount());

        【讨论】:

          【解决方案9】:
          class MyLayoutManager extends LinearLayoutManager {
          
            public MyLayoutManager(Context context) {
              super(context, LinearLayoutManager.VERTICAL, false);
            }
          
            @Override public void smoothScrollToPosition(RecyclerView recyclerView,
                final RecyclerView.State state, final int position) {
          
              int fcvip = findFirstCompletelyVisibleItemPosition();
              int lcvip = findLastCompletelyVisibleItemPosition();
          
              if (position < fcvip || lcvip < position) {
                // scrolling to invisible position
          
                float fcviY = findViewByPosition(fcvip).getY();
                float lcviY = findViewByPosition(lcvip).getY();
          
                recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
          
                  int currentState = RecyclerView.SCROLL_STATE_IDLE;
          
                  @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
          
                    if (currentState == RecyclerView.SCROLL_STATE_SETTLING
                        && newState == RecyclerView.SCROLL_STATE_IDLE) {
          
                      // recursive scrolling
                      smoothScrollToPosition(recyclerView, state, position);
                    }
          
                    currentState = newState;
                  }
          
                  @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
          
                    int fcvip = findFirstCompletelyVisibleItemPosition();
                    int lcvip = findLastCompletelyVisibleItemPosition();
          
                    if ((dy < 0 && fcvip == position) || (dy > 0 && lcvip == position)) {
                      // stop scrolling
                      recyclerView.setOnScrollListener(null);
                    }
                  }
                });
          
                if (position < fcvip) {
                  // scroll up
          
                  recyclerView.smoothScrollBy(0, (int) (fcviY - lcviY));
                } else {
                  // scroll down
          
                  recyclerView.smoothScrollBy(0, (int) (lcviY - fcviY));
                }
              } else {
                // scrolling to visible position
          
                float fromY = findViewByPosition(fcvip).getY();
                float targetY = findViewByPosition(position).getY();
          
                recyclerView.smoothScrollBy(0, (int) (targetY - fromY));
              }
            }
          }
          

          MyLayoutManager layoutManager = new MyLayoutManager(context);
          recyclerView.setLayoutManager(layoutManager);
          
          RecyclerView.Adapter adapter = new YourAdapter();
          recyclerView.setAdapter(adapter);
          
          recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1);
          

          上面的代码可以工作,但是不流畅也不酷。

          【讨论】:

            【解决方案10】:

            在 Kotlin 中:

            recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }
            
            private fun scrollToEnd() =
                    (adapter.itemCount - 1).takeIf { it > 0 }?.let(recyclerView::smoothScrollToPosition)
            

            【讨论】:

            • 哈,谢谢GlobalLayoutListener!重构后,我不得不使用你的方法来滚动。不要忘记像stackoverflow.com/questions/28264139/… 中那样删除监听器,否则你不会将 RecyclerView 滚动到开头。
            【解决方案11】:

            Roc 的回答很有帮助。我想给它加一个小块:

            mRecyclerView.scrollToPosition(mAdapter.getItemCount() - 1);
            

            【讨论】:

              【解决方案12】:

              在我的视图没有相同高度的情况下,调用 scrollToPosition on the LayoutManager 可以真正滚动到底部并完全看到最后一项:

              recycler.getLayoutManager().scrollToPosition(adapter.getItemCount() - 1);
              

              【讨论】:

                【解决方案13】:

                答案是

                recyclerView.scrollToPosition(arrayList.size() - 1);
                

                大家都提到过。

                但我面临的 问题没有正确放置

                我尝试将其放在adapter.notifyDataSetChanged(); 之后,它起作用了。 每当您的回收站视图中的数据发生变化时,它会自动滚动到底部,就像在发送消息或您第一次打开聊天列表之后一样。

                注意:这段代码是用 Java 测试的。

                我的实际代码是:

                //scroll to bottom after sending message.
                binding.chatRecyclerView.scrollToPosition(messageArrayList.size() - 1);
                

                【讨论】:

                  【解决方案14】:

                  如果您在使 recyclerview 滚动到最新版本时遇到问题,请检查 recyclerview 是否不在nestedScrollView 中。那是我的问题。我必须删除nestedScrollview,然后下面的代码才起作用。

                  binding.recyclerView.scrollToPosition(adapter.getItemCount() - 1);
                  

                  【讨论】:

                    【解决方案15】:

                    第一次进入回收站视图时第一次滚动然后使用

                    linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1
                    

                    向下滚动输入负号,向上滚动输入正值);

                    如果视图的高度非常大,那么 scrolltoposition 特定的偏移量将用于视图的顶部,那么您可以使用

                    int overallXScroldl =chatMessageBinding.rvChat.computeVerticalScrollOffset();
                    chatMessageBinding.rvChat.smoothScrollBy(0, Math.abs(overallXScroldl));
                    

                    【讨论】:

                      【解决方案16】:

                      这对我来说非常好:

                      AdapterChart adapterChart = new AdapterChart(getContext(),messageList);
                      recyclerView.setAdapter(adapterChart);
                      recyclerView.scrollToPosition(recyclerView.getAdapter().getItemCount()-1);
                      

                      【讨论】:

                        【解决方案17】:

                        此代码将首先为您提供最新帖子,我认为此答案很有帮助。

                            mInstaList=(RecyclerView)findViewById(R.id.insta_list);
                            mInstaList.setHasFixedSize(true);
                        
                            LinearLayoutManager layoutManager = new LinearLayoutManager(this);
                            layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
                        
                            mInstaList.setLayoutManager(layoutManager);
                            layoutManager.setStackFromEnd(true);
                            layoutManager.setReverseLayout(true);
                        

                        【讨论】:

                          【解决方案18】:

                          尝试了@galex 的方法,在重构之前一直有效。所以我使用了@yanchenko 的答案并进行了一些更改。这可能是因为我从onCreateView() 调用了滚动,其中构建了片段视图(并且可能没有正确的大小)。

                          private fun scrollPhotosToEnd(view: View) {
                              view.recycler_view.viewTreeObserver.addOnGlobalLayoutListener(object :
                                  ViewTreeObserver.OnGlobalLayoutListener {
                                  override fun onGlobalLayout() {
                                      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                                          view.recycler_view.viewTreeObserver.removeOnGlobalLayoutListener(this)
                                      } else {
                                          @Suppress("DEPRECATION")
                                          view.recycler_view.viewTreeObserver.removeGlobalOnLayoutListener(this)
                                      }
                                      adapter?.itemCount?.takeIf { it > 0 }?.let {
                                          view.recycler_view.scrollToPosition(it - 1)
                                      }
                                  }
                              })
                          }
                          

                          您还可以像https://stackoverflow.com/a/39001731/2914140 一样添加viewTreeObserver.isAlive 的检查。

                          【讨论】:

                            【解决方案19】:

                            如果你正在使用他,你必须在scrollToPosition之前隐藏AppBarLayout

                            【讨论】:

                              【解决方案20】:

                              可以滚动到最后一项的底部,如果最后一项的高度太大,需要偏移

                               private void scrollToBottom(final RecyclerView recyclerView) {
                                      // scroll to last item to get the view of last item
                                      final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                                      final RecyclerView.Adapter adapter = recyclerView.getAdapter();
                                      final int lastItemPosition = adapter.getItemCount() - 1;
                                  
                                      layoutManager.scrollToPositionWithOffset(lastItemPosition, 0);
                                      recyclerView.post(new Runnable() {
                                          @Override
                                          public void run() {
                                              // then scroll to specific offset
                                              View target = layoutManager.findViewByPosition(lastItemPosition);
                                              if (target != null) {
                                                  int offset = recyclerView.getMeasuredHeight() - target.getMeasuredHeight();
                                                  layoutManager.scrollToPositionWithOffset(lastItemPosition, offset);
                                              }
                                          }
                                      });
                              
                              
                              
                                  }
                              

                              【讨论】:

                              • layoutManger 没有 scrollToPositionWithOffset 的属性
                              • @ZahidAli 你可以在 LinearLayoutManager.java 中看到 scrollToPositionWithOffset 方法
                              【解决方案21】:

                              我知道这为时已晚,但这对我有用,所以我正在回答这个问题

                              首先,

                              LinearLayoutManager layoutManager=new LinearLayoutManager(this);
                              layoutManager.setStackFromEnd(true);
                              recyclerView.setLayoutManager(layoutManager);
                              

                              之后,

                              database.addValueEventListener(....){
                              @override
                              public void onDataCange(...){
                              ....
                              ....
                              //put this line here
                              recyclerView.smoothScrollToPosition(recyclerView.getAdapter().getItemCount()-1);
                              }
                              }
                              

                              【讨论】:

                                【解决方案22】:

                                只有伊恩的回答能够让我的RecyclerView 滚动到指定位置。但是,当我使用scrollToPosition() 时,RecyclerView 之后无法滚动。 smoothScrollToPosition() 有效,但是当列表很长时,初始动画使其太慢。 原因是监听器没有被移除。我使用下面的代码删除了当前的ViewTreeObserver,它起到了魅力。

                                    mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                                        @Override
                                        public void onGlobalLayout() {
                                            mRecyclerView.scrollToPosition(mPosition);
                                            mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                                        }
                                    });
                                

                                【讨论】:

                                  【解决方案23】:

                                  如果这些都不起作用,

                                  你应该尝试使用:

                                  ConstraintLayout targetView = (ConstraintLayout) recyclerView.findViewHolderForAdapterPosition(adapter.getItemCount()-1).itemView;
                                  targetView.getParent().requestChildFocus(targetView, targetView);
                                  

                                  通过这样做,您请求显示某个 ConstraintLayout(或您拥有的任何内容)。滚动是即时的。

                                  即使显示键盘,我也能工作。

                                  【讨论】:

                                    【解决方案24】:

                                    如果您只想从最终用途开始:

                                    layoutManager.stackFromEnd = true
                                    

                                    (Kotlin 解决方案)

                                    【讨论】:

                                      【解决方案25】:

                                      如果你使用 kotlin,试试这个

                                      conversationView.post {
                                          scrollToPosition(conversationView?.adapter.itemCount - 1)
                                      }
                                      

                                      【讨论】:

                                        【解决方案26】:

                                        如果您使用 reversedLayout “聊天界面”,特别是如果您有图像,您可以尝试以下操作:

                                        首先,向适配器注册DataObserver

                                           chatAdapter.registerAdapterDataObserver(object : 
                                                                 RecyclerView.AdapterDataObserver() {
                                        
                                           override fun onItemRangeInserted(positionStart: Int itemCount: Int) {
                                        
                                              val messageCount: Int = chatAdapter.itemCount
                                        
                                              val lastVisiblePosition: Int =
                                                    layoutManager.findLastCompletelyVisibleItemPosition()
                                        
                                                 if (lastVisiblePosition == -1 || positionStart <= (messageCount - 1)) {
                                                    recyclerView.scrollToPosition(positionStart)
                                                }
                                            }
                                        }) 
                                        

                                        一旦您为适配器设置了数据,就会调用它

                                        其次,你必须像这样向 recyclerView 添加一个滚动监听器:

                                           recyclerView.addOnScrollListener(object 
                                             :RecyclerView.OnScrollListener() {
                                        
                                               override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: 
                                                  Int) {
                                                    super.onScrolled(recyclerView, dx, dy)
                                        
                                                  val lastVisiblePosition: Int =
                                                      layoutManager.findLastCompletelyVisibleItemPosition()
                                                if (lastVisiblePosition != -1 && lastVisiblePosition < 
                                                        chatAdapter.itemCount - 1) {
                                                    recyclerView.smoothScrollToPosition(0)
                                                }
                                            }
                                        })
                                        

                                        【讨论】:

                                          【解决方案27】:

                                          只使用重力来轻松解决

                                          android:layout_gravity="bottom"

                                          【讨论】:

                                            猜你喜欢
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 2021-08-24
                                            • 2023-02-01
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 1970-01-01
                                            • 1970-01-01
                                            相关资源
                                            最近更新 更多