Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView: scrollToPosition not working

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.

like image 586
Bathilde Rocchia Avatar asked Jun 05 '16 16:06

Bathilde Rocchia


2 Answers

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.

like image 186
林果皞 Avatar answered Oct 20 '22 14:10

林果皞


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

like image 32
user35603 Avatar answered Oct 20 '22 13:10

user35603