简体   繁体   English

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

[英]How to scroll to the bottom of a RecyclerView? scrollToPosition doesn't work

I'd like to scroll to the bottom of the RecyclerView list after loading the activity.我想在加载活动后滚动到 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(...) throws an exception about being not supported in RecyclerView, and conversationView.scrollToPosition(...) doesn't seem to do anything. conversationView.scrollTo(...)抛出 RecyclerView 不支持的异常,而conversationView.scrollToPosition(...)似乎没有做任何事情。

After the above block of code, I added在上面的代码块之后,我添加了

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

which doesn't work.这是行不通的。 There are 30 elements in GENERIC_MESSAGE_LIST . GENERIC_MESSAGE_LIST中有 30 个元素。

I was looking at this post to find the answer but... I think everyone on this post was facing the same scenario as me: scrollToPosition() was fully ignored, for an evident reason.我正在查看这篇文章以寻找答案,但是......我认为这篇文章中的每个人都面临着与我相同的情况: scrollToPosition()被完全忽略,原因很明显。

What I was using?我用的是什么?

recyclerView.scrollToPosition(items.size());

... what WORKED ? ...什么工作

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

Just set setStackFromEnd=true or setReverseLayout=true so that LLM will layout items from end.只需设置setStackFromEnd=truesetReverseLayout=true以便法学硕士将从末尾开始布局项目。

The difference between these two is that setStackFromEnd will set the view to show the last element, the layout direction will remain the same.这两者的区别在于setStackFromEnd会将视图设置为显示最后一个元素,布局方向将保持不变。 (So, in an left-to-right horizontal Recycler View, the last element will be shown and scrolling to the left will show the earlier elements) (因此,在从左到右的水平 Recycler View 中,将显示最后一个元素,向左滚动将显示较早的元素)

Whereas setReverseLayout will change the order of the elements added by the Adapter.setReverseLayout将改变由 Adapter 添加的元素的顺序。 The layout will start from the last element, which will be the left-most in an LTR Recycler View and then, scrolling to the right will show the earlier elements.布局将从最后一个元素开始,这将是 LTR 回收器视图中最左侧的元素,然后向右滚动将显示较早的元素。

Sample:样本:

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

See documentation for details.有关详细信息,请参阅文档

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

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

To scrolldown from any position in the recyclerview to bottom从回收站视图中的任何位置向下滚动到底部

Kotlin科特林

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

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

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

When you call setAdapter , that does not immediately lay out and position items on the screen (that takes a single layout pass) hence your scrollToPosition() call has no actual elements to scroll to when you call it.当您调用setAdapter时,它不会立即在屏幕上布置和定位项目(需要一次布局传递),因此您的scrollToPosition()调用在您调用它时没有要滚动到的实际元素。

Instead, you should register a ViewTreeObserver.OnGlobalLayoutListener (via addOnGlobalLayoutListner() from a ViewTreeObserver created by conversationView.getViewTreeObserver() ) which delays your scrollToPosition() until after the first layout pass:相反,您应该注册一个ViewTreeObserver.OnGlobalLayoutListener (通过从由sessionView.getViewTreeObserver()创建的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.
  }
});

Solution for Kotlin : Kotlin 的解决方案:

apply below code after setting "recyclerView.adapter" or after "recyclerView.adapter.notifyDataSetChanged()"在设置“recyclerView.adapter”或“recyclerView.adapter.notifyDataSetChanged()”后应用以下代码

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

如果您的适配器与 recyclerView 相连,则只需执行此操作。

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

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

and

MyLayoutManager layoutManager = new MyLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);

RecyclerView.Adapter adapter = new YourAdapter();
recyclerView.setAdapter(adapter);

recyclerView.smoothScrollToPosition(adapter.getItemCount() - 1);

above code works, but it's not smooth and not cool.上面的代码有效,但不流畅也不酷。

In Kotlin:在科特林:

recyclerView.viewTreeObserver.addOnGlobalLayoutListener { scrollToEnd() }

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

Roc answer is a great help. Roc 的回答很有帮助。 I would like to add a small block to it:我想给它添加一个小块:

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

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

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

First time scroll when entering in recycler view first time then use第一次进入回收器视图时第一次滚动然后使用

linearLayoutManager.scrollToPositionWithOffset(messageHashMap.size()-1

put in minus for scroll down for scroll up put in positive value);输入减号向下滚动向上滚动输入正值);

if the view is very big in height then scrolltoposition particular offset is used for the top of view then you use如果视图的高度非常大,则 scrolltoposition 特定偏移用于视图的顶部,然后您使用

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

This works perfectly fine for me:这对我来说非常好:

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

The answer is答案是

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

Everyone has mentioned it.每个人都提到过。

But the problem I was facing was that it was not placed correctly .我面临问题是它没有正确放置

I tried and placed just after the adapter.notifyDataSetChanged();我尝试并放置在adapter.notifyDataSetChanged();之后adapter.notifyDataSetChanged(); and it worked.它奏效了。 Whenever, data in your recycler view changes, it automatically scrolls to the bottom like after sending messages or you open the chat list for the first time.每当您的回收站视图中的数据发生变化时,它会自动滚动到底部,就像在发送消息后或您第一次打开聊天列表一样。

Note : This code was tasted in Java.注意:此代码是在 Java 中试用的。

actual code for me was :我的实际代码是:

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

If you are having issues making recyclerview scroll to the latest, check that the recyclerview is not inside a nestedScrollView.如果您在使 recyclerview 滚动到最新时遇到问题,请检查 recyclerview 是否不在nestedScrollView 内。 That was my issue.那是我的问题。 I had to remove the nestedScrollview and then the below code worked.我不得不删除nestedScrollview,然后下面的代码工作。

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

this code will give you latest post first, i think this answer is helpful.此代码将首先为您提供最新帖子,我认为此答案很有帮助。

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

Tried a method of @galex , it worked until refactoring.尝试了@galex 的一种方法,它一直工作到重构。 So I used an answer of @yanchenko and changed a bit.所以我使用了@yanchenko的答案并进行了一些更改。 Probably this is because I called scrolling from onCreateView() , where a fragment view was built (and probably didn't have right size).可能这是因为我从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)
            }
        }
    })
}

You can also add a check of viewTreeObserver.isAlive like in https://stackoverflow.com/a/39001731/2914140 .您还可以在https://stackoverflow.com/a/39001731/2914140 中添加对viewTreeObserver.isAlive的检查。

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

You can scroll to the bottom of the last item, if the height of the last item is too large, you need to offset可以滚动到最后一项的底部,如果最后一项的高度太大,需要偏移

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



    }

Only Ian's answer was able to make my RecyclerView scroll to a specified position.只有 Ian 的回答能够使我的RecyclerView滚动到指定位置。 However, The RecyclerView was not able to scroll afterwards when I used scrollToPosition() .但是,当我使用scrollToPosition()时, RecyclerView之后无法滚动。 smoothScrollToPosition() worked but the initial animation made it too slow when the list was long. smoothScrollToPosition()起作用了,但是当列表很长时,初始动画使它变得太慢。 The reason was the listener was not removed.原因是监听器没有被移除。 I used the code below to remove the current ViewTreeObserver and it worked as a charm.我使用下面的代码删除了当前的ViewTreeObserver ,它起到了ViewTreeObserver的作用。

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

If none of these worked,如果这些都不起作用,

you should try to use :你应该尝试使用:

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

By doing this, you are requesting a certain ConstraintLayout (Or whatever you have) to be displayed.通过这样做,您请求显示某个 ConstraintLayout(或任何您拥有的)。 The scroll is instant.滚动是即时的。

I works even with keyboard shown.即使显示键盘,我也能工作。

If you want just starting from the end use:如果您只想从最终使用开始:

layoutManager.stackFromEnd = true

(Kotlin solution) (科特林解决方案)

I know this is too late but this worked for me so that i am answering this question我知道这太晚了,但这对我有用,所以我正在回答这个问题

first,第一的,

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

after that,之后,

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

if you use kotlin, try this如果你使用 kotlin,试试这个

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

If you working with reversedLayout "chat interface" specially if you have images, you can try the following:如果你特别使用 reversedLayout “聊天界面”,如果你有图像,你可以尝试以下操作:

first, register DataObserver with the adapter首先,向适配器注册 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)
        }
    }
}) 

this will be called once you set the data for the adapter一旦您为适配器设置数据,这将被调用

second, you have to add a scroll listener to the recyclerView like this:其次,你必须像这样向 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)
        }
    }
})

use video_frames?.smoothScrollToPosition(pos.toInt())使用 video_frames?.smoothScrollToPosition(pos.toInt())

this work for me*这对我有用*

use only gravity for easy solution仅使用重力来轻松解决

android:layout_gravity="bottom"机器人:layout_gravity="底部"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM