如何滚动到RecyclerView的底部?scrollToPosition不起作用


160

加载活动后,我想滚动到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)

这不起作用。中有30个元素GENERIC_MESSAGE_LIST


scrollToPosition设置适配器后,您会立即打电话吗?
ianhanniballake 2014年

@ianhanniballake是的,就在之后conversationView.setAdapter(conversationViewAdapter);
waylaidwanderer 2014年

4
可能是因为您正在调用+1而不是-1,所以第5个元素将位于位置[4],因为它是从0开始的。执行+1会给您一个ArrayOutOfBounds ... ...它没有在那里崩溃吗?
RCB

1
在setadapter mRecyclerView.scrollToPosition(carsList.size()-1)之后添加;
Webserveis

Answers:


194

只需设置setStackFromEnd=truesetReverseLayout=true使LLM将布局从最终产品。

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

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

样品:

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

有关详细信息,请参见文档


132
这可能很有用,但是似乎无法回答所提出的问题。
约瑟夫2015年

6
如果同时设置stackFromEnd = true和setReverseLayout = true,则此方法无效。有人知道任何解决方法吗?
Luccas Correa

10
对于聊天视图,linearLayoutManager.setStackFromEnd(true)有效。
Muneeb Mirza

7
这根本没有解决这个问题。
轨道

1
仅在视图满之前可用。它不会滚动,只是从底部堆叠。
MiguelSlv '17

376

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

我在用什么?

recyclerView.scrollToPosition(items.size());

...什么工作

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

6
recyclerView.scrollToPosition(messages.size()-1);真的对我有用,我只是想知道原因。
引擎

25
这对我有用。实际上这是有道理的,因为Java列表是从零开始的。例如列表[0,1,2]的尺寸为3,但最后的位置是2,因为它从0开始
卡尔文

19
仅供参考,有一个平滑的滚动smoothScrollToPosition(...)
伊万·莫里约

5
@Ivan Morgilo smoothScrollToPosition根本不起作用:S
cesards's

2
@IvanMorgillo-您的答案似乎对想要的结果和更平滑的= D都更好。似乎滚动到位置对我来说有点麻烦(我猜是在调用的函数和视图的创建之间有一个小的延迟?反正它不能正常工作)
Roee

53

我知道现在回答晚了,如果有人想知道下面的解决方案

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

7
conversationView.smoothScrollToPosition(conversationView.getAdapter().getItemCount() **-1**);列表中的位置应从零开始。
贝拉克

2
如果最后一行的高度大于屏幕高度,这将无济于事。
Anuj Jindal

这就是我所需要的。尽管我必须将其包装在处理程序后延迟中才能使所有功能正常运行。
马克·亚历山大

18

从回收站视图中的任何位置向下滚动到底部

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

15

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

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

10

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

相反,你应该注册一个ViewTreeObserver.OnGlobalLayoutListener(通过addOnGlobalLayoutListner()ViewTreeObserver被创造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.
  }
});

1
谢谢,但这不起作用。它通过停止快速移动来中断滚动,如果您拖动鼠标,滚动速度会异常快。
waylaidwanderer 2014年

听起来您没有删除监听器。它应该只被调用一次(在元素第一次被布置之后),而不是在滚动过程中被调用。
ianhanniballake 2014年

OnGlobalLayoutListener可能因为您正在检查而没有被删除vto.isAlive()。我不知道原因,但是ViewTreeObserver有时会更改为View,所以与其进行isAlive更好的检查,不如直接ViewTreeObserverconversationView.getViewTreeObserver().removeGlobalOnLayoutListener
他人交流

@ianhanniballake我建议进行修改以解决此问题。
萨凯(Saket)

6

Kotlin解决方案:

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

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

5

在科特林:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

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

哈,谢谢GlobalLayoutListener!重构后,我不得不使用您的方法进行滚动。不要忘记像stackoverflow.com/questions/28264139/…中那样删除侦听器,否则您不会将RecyclerView滚动回到开头。
CoolMind

4
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);

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



4

大鹏的答案是很大的帮助。我想添加一个小块:

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


2

这对我来说很好用:

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

2

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

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

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

如果视图的高度很大,则使用scrolltoposition特定的偏移量作为视图的顶部,然后使用

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

0

只有Ian的答案才能使我RecyclerView滚动到指定位置。但是,RecyclerView当我使用时,The 之后无法滚动scrollToPosition()smoothScrollToPosition()可以,但是当列表很长时,初始动画使它太慢了。原因是未删除监听器。我使用下面的代码删除了当前代码ViewTreeObserver,它起到了很好的作用。

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

0

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

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

0

尝试了@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)
            }
        }
    })
}

您还可以viewTreeObserver.isAlivehttps://stackoverflow.com/a/39001731/2914140中添加类似的支票。


0

如果这些都不起作用,

您应该尝试使用:

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.