3

I saw many related questions about this topic, but none of them helped me. I am using a StaggeredGridLayout for my RecyclerView (showing cards). Every CardView opens a new activity, the problem I am facing is that on back press from that new activity, the RecyclerView scrolls back to the top. I want it to retain the previously scrolled position.

This what I use for now to set the layout :

rv.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
rv.setHasFixedSize(true);

Any proper working solution for a newbie?

EDIT :

   @Override
 protected void onResume() {
     super.onResume();
    if (listState != null) {
        rvlayout.onRestoreInstanceState(listState);
    }
      initializeData();

 }

Layout :

RecyclerView.LayoutManager rvlayout = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);

and initializeData :

 public void initializeData() {

    rv.setLayoutManager(rvlayout);
    rv.setHasFixedSize(true);
    persons.clear();

    c = db.rawQuery(SELECT_SQL, null);
    c.moveToFirst();

    if (!c.moveToFirst())
    return;


    while (!c.isLast()) {
        id = c.getString(0);
        names = c.getString(1);
        age = c.getString(2);
        persons.add(new Person(id,names,age));
        c.moveToNext();
    }

    adapter = new RVAdapter(persons);
    rv.setAdapter(adapter);

}
  • please post your code – JAAD Jan 28 '16 at 07:28
  • you don't have to call initializeData() in onResume(), I guess you have call to this method in onCreate() too. So they are getting called twice. Do you have call to initializeData() in onCreate() and onResume() ? – capt.swag Jan 28 '16 at 09:31
  • No, I only call it onResume(), so that it only gets called once. –  Jan 28 '16 at 09:37

5 Answers5

8

The proper way to do it is to save the state of RecyclerView scroll position and restore it while switching between Activities.

1. To save it, override onSaveInstanceState()

protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);

    state.putParcelable(LIST_STATE_KEY, layoutManager.onSaveInstanceState());
}

2. To retrieve the data override onRestoreInstanceState()

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);

    Parcelable listState = state.getParcelable(LIST_STATE_KEY);
}

3. And finally update the LayoutManager in onResume() method

@Override
protected void onResume() {
    super.onResume();

    if (listState != null) {
        layoutManager.onRestoreInstanceState(listState);
    }
}
capt.swag
  • 10,335
  • 2
  • 41
  • 41
  • I am a beginner @4k3R. Can you explain your code a bit, I mean like where to place it? And what is LIST_STATE_KEY? A string? What is its value? –  Jan 28 '16 at 09:09
  • 1
    LIST_STATE_KEY is a string, and it can be any value, the thing is putParcelable needs a key value pair. So the key should be unique. And so I used a variable LIST_STATE_KEY to store the key value. Eg: String LIST_STATE_KEY = "list_state"; and this variable should be global, so it can be used everywhere in your code. – capt.swag Jan 28 '16 at 09:09
  • I just tried your code, I realized it retains the scroll position but it updates my recyclerview twice with data? It used to happen when I used to initialize my data twice (inside onCreate and onResume). I updated the question with my code. How am I initializing it twice? –  Jan 28 '16 at 09:22
1

If you don't load the list again (when press back button) the recycler view scroll should be at the same position. Do you reload the list in onResume()?

UPDATE

I've just tried the solution that I mentioned in the comment below but with no results. I hope this code example will help you:

private int scrollPosition = 0;
private boolean shouldKeepScrollPosition = true;

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if(shouldKeepScrollPosition) {
                MyFragment.this.scrollPosition += dy;
            }
        }
    });

Before reload data do this:

shouldKeepScrollPosition = false;

After reload data:

shouldKeepScrollPosition = true;
recyclerView.scrollBy(0, scrollPosition);
David Rauca
  • 1,583
  • 1
  • 14
  • 17
  • Yes, I do reload the data. I need to show the updated RecyclerView on backpress. –  Jan 28 '16 at 09:10
  • ok. Before reload data, keep the scroll position using recyclerView.computeVerticalScrollOffset(). After reload data, scroll the recycler view to that value using scrollToPosition. – David Rauca Jan 28 '16 at 09:26
  • It would be highly appreciated If you can post an example code of what you just wrote @David –  Jan 28 '16 at 09:28
0

No idea why that's happening... but you can probably make use of startActivityForResult. Make sure you save the clicked position in the list before calling the method.

Override onActivityResult in that Activity that has your grid layout and call mRecyclerView.scrollToPosition(mLastScrolledPosition) there.

Jagoan Neon
  • 1,072
  • 11
  • 13
0

use
rv.smoothScrollToPosition(position); or
rv.scrollToPosition(position);

to maintain scroll position . when u open new activity save position in a variable , sharedpreference . and call rv.smoothScrollToPosition(position)or rv.scrollToPosition(position) in initializeData();

JAAD
  • 12,349
  • 7
  • 36
  • 57
  • How can I save the position in a variable? Can you post an example code? –  Jan 28 '16 at 13:16
0

Try to add setRetainInstance(true) in your Oncreate fragment method like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

The setRetainInstance(boolean retain) method Controls whether a fragment instance is retained across Activity re-creation (such as from a configuration change).

Badr
  • 2,021
  • 1
  • 21
  • 27