16

I have a simple project displaying user text messages. I am currently not able to scroll the RecyclerView to the last received text message.
My recyclerView is inside a very simple activity using the Coordinator layout:

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:background="@color/background" tools:context="com.myproject.myproject.MessageActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay"/> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_message" /> </android.support.design.widget.CoordinatorLayout> 

And the content_message.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:descendantFocusability="beforeDescendants" android:focusable="true" android:focusableInTouchMode="true" android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context="com.textbutler.textbutler.MessageActivity" tools:showIn="@layout/activity_message"> <!-- some views here --> <android.support.v7.widget.RecyclerView android:id="@+id/content_text" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:scrollbars="vertical" /> <!-- some views here --> </LinearLayout> 

The activity load the data through a basic loader. I force the access to the recyclerview on the UI thread and with a delay (just in case)

public class MessageActivity extends AppCompatActivity { private static final int URL_LOADER = 0; private RecyclerView recyclerView; private LinearLayoutManager layoutManager; private MessageAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_message); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); initializeList(savedInstanceState); } private void initializeList(Bundle savedInstanceState) { if (recyclerView == null) { recyclerView = (RecyclerView) findViewById(R.id.content_text); if (recyclerView == null) return; if (layoutManager == null) { layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); layoutManager.setReverseLayout(true); layoutManager.setStackFromEnd(true); } recyclerView.setLayoutManager(layoutManager); final Activity me = this; if (adapter == null) { adapter = new MessageAdapter(new ObservableCallback() { @Override public void callback() { // this function is called right after the recyclerview received notifyDataSetChanged new Handler().postDelayed(new Runnable() { @Override public void run() { me.runOnUiThread(new Runnable() { @Override public void run() { Log.d("ScrollingUI", "scrolltoposition " + adapter.getItemCount()); recyclerView.scrollToPosition(adapter.getItemCount() - 1); } }); } }, 1000); } }); SMSByUserLoader loader = new SMSByUserLoader(this, adapter); getSupportLoaderManager().initLoader(URL_LOADER, savedInstanceState, loader); } recyclerView.setAdapter(adapter); } } 

}

When I run this code, the RecyclerView is perfectly filled. After a second I have the correct log but the view does not changed. I already found various answers to this issue, but none of them seems to work in my case.
Scrolling in the RecyclerView perfectly works.

2
  • Try recyclerView.getLayoutManager().scrollToPosition(adapter.getItemCount()-1) or recyclerView.getLayoutManager().smoothScrollToPosition(...) Commented Jun 5, 2016 at 17:16
  • I already tried and nothing happened Commented Jun 6, 2016 at 17:38

7 Answers 7

15

I have the same problem and smoothScrollToPosition method solve it:

 RecyclerView.smoothScrollToPosition(postion); 
Sign up to request clarification or add additional context in comments.

1 Comment

SmoothScrollToPosition does solve this issue, however, for very large lists this is a terrible UX experience.
10

Inspired by @omid-jafarinezhad, I need to use smoothScrollToPosition, but I still need it instant scroll to the end because it will looks weird if keep scrolling many items slowly. And my workaround is use both methods at sequence, i.e.:

 myRecyclerView.scrollToPosition(position); new Handler().postDelayed(new Runnable() { @Override public void run() { myRecyclerView.smoothScrollToPosition(position); } }, 50); //sometime not working, need some delay 

This may seems like a hack but at least solve my problem at this time.

3 Comments

Lol! I see what you are trying to do. But this solution still will not work at times.
tks for your answer,it really solve my problem.but I've no idea why it occurs like this
This answer really helped, Thanks a lot 林果皞 and stackoverflow
8

My problem was that my RecyclerView was inside a NestedScrollView. I removed the NestedScrollView, and scrollToPosition() worked.

4 Comments

How can we fix this without removing the nested recycler view ?
It is the NestedScrollView that I removed
Sorry. Actually my question was, how to fix this issue without removing the NestedScrollView ?
The scrolling should be done on the NestedScrollView. See: stackoverflow.com/a/76544884/1934673
7

You used layoutManager.setReverseLayout(true) and layoutManager.setStackFromEnd(true) in order to start your list from the end. So, if I understand it correctly, the list starts now from position adapter.getItemCount() - 1 and your list shouldn't scroll. Maybe you should try ...scrollToPosition(0).

Comments

1
 private val delayShowRunnable by lazy{ object:Runnable{ override fun run() { rv_content?.alpha=1f } } } private val linearSmoothScroller by lazy{ object: LinearSmoothScroller(context){ override fun onTargetFound(targetView: View, state: RecyclerView.State, action: Action) { super.onTargetFound(targetView, state, action) rv_content?.postDelayed(delayShowRunnable,50) } override fun calculateTimeForDeceleration(dx: Int): Int { return 1//set max speed } override fun calculateTimeForScrolling(dx: Int): Int { return 1 //set max speed } } } rv_content?.alpha=0f //hide listview before scroll finish linearSmoothScroller.targetPosition = pos (rv_content?.layoutManager as LinearLayoutManager?)?.startSmoothScroll(linearSmoothScroller) 

scroll position change will not be reflected until the next layout call. so we should use <smoothscrollToPosition()> , find out target view.

Comments

1

Always use -1 if you want to scroll to the end of your adapter recyclerView.scrollToPosition(chatAdapter.getItemCount()-1);

Comments

0
  1. In a usual case you can wait some time till a RecyclerView can scroll.

    binding.recyclerView.postDelayed({ binding.recyclerView.scrollToPosition(adapter.itemCount - 1) }, 100) 

Instead of 100 ms you can use numbers from 0 to 1000, depending on a device.

  1. If you use Paging adapter it can request a new page. When you try to scroll a RecyclerView the adapter isn't ready (new items haven't not been added). So you can add a subscriber to request finish and then scroll the list (probably after a pause like in the first case).

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.