如何滚动到 RecyclerView 的底部?滚动到位置不起作用
Posted
技术标签:
【中文标题】如何滚动到 RecyclerView 的底部?滚动到位置不起作用【英文标题】:How to scroll to the bottom of a RecyclerView? scrollToPosition doesn't work 【发布时间】: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);
【参考方案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=true
或 setReverseLayout=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
!重构后,我不得不使用你的方法来滚动。不要忘记像***.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://***.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"
【讨论】:
以上是关于如何滚动到 RecyclerView 的底部?滚动到位置不起作用的主要内容,如果未能解决你的问题,请参考以下文章
Android-->RecyclerView 显示底部,滚动底部(无动画)
没有滚动时屏幕底部的 Recyclerview 页脚和滚动时位于列表末尾的 Recyclerview 页脚