Skip to content Skip to sidebar Skip to footer

How To Retain Recyclerview's Position After Orientation Change, While Using Firebase & Childeventlistener?

I'm working on a simple APOD app that implements: RecyclerView CardView Firebase Picasso The app grabs images and text from Firebase and Firebase Storage, displays them in a Card

Solution 1:

While rotation activity get destroyed and re-created once again that means all your objects destroyed and re-created and also layout re-drawn again.

It you want to stop re-creation of activity you can use android:configChanges but it is bad practice and also not the right way.

Only thing remains now is to save position of recyclerview before rotation and get it after activity re-created.

// to save recyclerview position

@OverridepublicvoidonSaveInstanceState(Bundle savedInstanceState) {
      // Save UI state changes to the savedInstanceState.// This bundle will be passed to onCreate if the process is// killed and restarted.
      savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here.super.onSaveInstanceState(savedInstanceState);
    }

// to restore value

@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    setContentView(R.layout.activity_main);
    getWindow().setBackgroundDrawable(newColorDrawable(Color.TRANSPARENT));

    Toolbartoolbar= (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    finalintcolumns= getResources().getInteger(R.integer.gallery_columns);

    mDatabaseReference = FirebaseDatabase.getInstance().getReference();
    query = mDatabaseReference.child("apod").orderByChild("date");

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(newGridLayoutManager(this, columns));


    mRecyclerAdapter = newRecyclerAdapter(this, query);
    mRecyclerView.setAdapter(mRecyclerAdapter);
   if(savedInstanceState != null){
     // scroll to existing position which exist before rotation.
     mRecyclerView.scrollToPosition(savedInstanceState.getInt("position"));
   }  

  }

Hope these help you.

Solution 2:

EDIT:

I was finally able to make this work using multiple factors. If any one of these were left out, it simply would not work.

Create new GridLayoutManager in my onCreate

gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns);

Save the RecyclerView state in onPause

privatefinalStringKEY_RECYCLER_STATE="recycler_state"; 
privatestatic Bundle mBundleRecyclerViewState;
privateParcelablemListState=null;

@OverrideprotectedvoidonPause() {
    super.onPause();

    mBundleRecyclerViewState = newBundle();
    mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
    mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
}

Include configChanges in the AndroidManifest.xml

Saving and restoring the RecyclerView state was not enough. I also had to go against the grain and change my AndroidManifest.xml to 'android:configChanges="orientation|screenSize"'

<activityandroid:name=".MainActivity"android:configChanges="orientation|screenSize">

Restore the RecyclerView state in onConfigurationChanged using a Handler

The handler was one of the most important parts, if it's not included, it simply will not work and the position of the RecyclerView will reset to 0. I guess this has been a flaw going back a few years, and was never fixed. I then changed the GridLayoutManager's setSpanCount depending on the orientation.

@OverridepublicvoidonConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    columns = getResources().getInteger(R.integer.gallery_columns);

    if (mBundleRecyclerViewState != null) {
        newHandler().postDelayed(newRunnable() {
            @Overridepublicvoidrun() {
                mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
                mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);
            }
        }, 50);
    }

    // Checks the orientation of the screenif (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        gridLayoutManager.setSpanCount(columns);
    } elseif (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        gridLayoutManager.setSpanCount(columns);
    }
    mRecyclerView.setLayoutManager(gridLayoutManager);
}

Like I said, all of these changes needed to be included, at least for me. I don't know if this is the most efficient way, but after spending many hours, it works for me.

Solution 3:

Activity gets recreated every time there is an orientation change. So you will have to save the position in the bundle and then re-use it when the onCreate() gets called the 2nd time.

@OverridepublicvoidonCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   int recyclerViewPosition;
   if(savedInstanceState != null){
     recyclerViewPosition = savedInstanceState.getInt(RECYCLER_VIEW_POSITION);
   }

   mRecyclerView.scrollToPosition(recyclerViewPosition);

}

@OverridepublicvoidonSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //save the current recyclerview position 
    outState.putInt(RECYCLER_VIEW_POSITION, mRecyclerView.getAdapterPosition());
}

You may want to have a look at RecyclerView.State() link for more options on how to restore the recycler view's state.

Solution 4:

Actually there is much better approach to restore the recycler view position when activity recreates, and this is by using the recycler view layout manager option to store and restore the last position (the last recycler view layout state). Take a look this tutorial.

With a few words, you need to call the layout manager methods onSaveInstanceState() and onRestoreInstanceState(Parcelable state) inside the corresponding activity methods. At the end you have to restore the layout manager state right after you’ve populated the adapter with data.

If you take a look inside the implementation of LinearLayoutManager for example, you will see how this class manage the state (store / restore)

Solution 5:

I finaly did it. Without hacks or change in manifest.xml. After long time of reading i found the key point :

So if you are loading your data asynchronously on each screen rotation and then posting it to the Main thread through the Looper queue, you will always end up with losing the previous scroll position. In fact, any

handler.post(this::setListItems);

will break it as well, because this action will be posted into the queue and delivered after the list has already started its layout.

As a result you have to use Handler or cache data if you want restoring recycler view state. Here is the code sample (androidx fragment).

privatestaticStringSTATE = "STATE";
    
    // the recycler view managerprivateGridLayoutManager layoutManager;
    
    @OverridepublicvoidonSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        // save recycler view layoutif(layoutManager != null) {
            outState.putParcelable(STATE, layoutManager.onSaveInstanceState());
        }
    }
    
    
    @OverridepublicViewonCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
                             
        ...
        

        loadDataAsync(savedInstanceState);
        
    
        return view;
    }
    
    privatevoidloadDataAsync(Bundle savedInstanceState){
    
        repository.getAll(new IMyListener()){
                @OverridepublicvoidgetAll(List<String> listResult) {
                        reyclerViewFilterableAdapter.setItems(listResult);
                        reyclerViewFilterableAdapter.notifyDataSetChanged();
                        newHandler().postDelayed(newRunnable() {

                            @Overridepublicvoidrun() {
                                if (layoutManager != null) {
                                    layoutManager.onRestoreInstanceState(savedInstanceState.getParcelable(STATE));
                                }
                            }
                        }, 100);
                }
            });
    
    }

Source : https://medium.com/@dimezis/android-scroll-position-restoring-done-right-cff1e2104ac7

Post a Comment for "How To Retain Recyclerview's Position After Orientation Change, While Using Firebase & Childeventlistener?"