Home » Android » android – How to save and restore scrolling position of the RecyclerView in a fragment when coming back from other activity to the same fragment?

android – How to save and restore scrolling position of the RecyclerView in a fragment when coming back from other activity to the same fragment?

Posted by: admin June 15, 2020 Leave a comment

Questions:

Request to moderators: This is not a duplicate question, please read the below information.

Before asking this question, i’d tried almost every available solution on SO, but none of them worked for me, some resulted in crash and some didn’t work at all (I’m a beginner, it is possible that i’ve done something wrong in my code, i’m not blaming anyone’s answer available on SO for not working in my case). Below are my codes, please have a look:

fragment_tab1.xml (which contains recyclerview):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:background="#333333"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_marginTop="96dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="42dp"
            android:background="@color/colorPrimaryDark"

            android:id="@+id/sort1"
            android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/sort"
            android:layout_marginLeft="10dp"
            android:fontFamily="@font/quicksand"
            android:textStyle="bold"
            android:text="Sort by:"
            android:textColor="#ffffff"/>

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                style="@style/Widget.AppCompat.Button.Colored"
                android:text="Oldest first"/>
            <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
                style="@style/Widget.AppCompat.Button.Colored"
            android:text="Newest first"/>
        </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_below="@id/sort1"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dp"
        android:padding="5dp" />

    </RelativeLayout>

</LinearLayout>

Fragmenttab1.java

public class tab1  extends Fragment {
    RecyclerView mRecyclerView;
    FirebaseDatabase mFirebaseDatabase;
    DatabaseReference mRef;
    LinearLayoutManager manager;
    private static final String TAG = "tab1";
    ProgressDialog progressDialog;
    View rootView;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        //Returning the layout file after inflating
        //Change R.layout.tab1 in you classes
         rootView = inflater.inflate(R.layout.fragment_tab1, container, false);


return rootView;

    }


    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);



        manager = new LinearLayoutManager(getActivity());
        mRecyclerView.setLayoutManager(manager);
        mRecyclerView.setItemViewCacheSize(20);
        mRecyclerView.setDrawingCacheEnabled(true);
        mFirebaseDatabase = FirebaseDatabase.getInstance();
        mRef = mFirebaseDatabase.getReference("memes");

        mFirebaseDatabase.getInstance().getReference().keepSynced(true);

        progressDialog = new ProgressDialog(getActivity());
        progressDialog.setMessage("Please Wait");
        progressDialog.show();

    }

    public void onStart() {

        super.onStart();


        FirebaseRecyclerAdapter<Model, ViewHolder> firebaseRecyclerAdapter =
                new FirebaseRecyclerAdapter<Model, ViewHolder>(
                        Model.class,
                        R.layout.row2,
                        ViewHolder.class,
                        mRef

                ) {



                    @Override
                    protected void populateViewHolder(ViewHolder viewHolder, Model model, int position) {
                        viewHolder.setDetails(getActivity(), model.getTitle(), model.getDesc(), model.getImage());
                        progressDialog.cancel();
                    }



                    @Override
                    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

                        ViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);

                        viewHolder.setOnClickListener(new ViewHolder.ClickListener(){




                            @Override
                            public void onItemClick(View view, int position){



                                //get data from firebase here
                                String mTitle = getItem(position).getTitle();
                                String mDesc= getItem(position).getDesc();
                                String mImage = getItem(position).getImage();

                                //pass this data to new activity
                                Intent intent = new Intent(view.getContext(), ArticleDetail.class);
                                intent.putExtra("title", mTitle);// put title
                                intent.putExtra("desc", mDesc);// put description
                                intent.putExtra("image", mImage); //put image urls

                                startActivity(intent);
                            }




                            @Override
                            public void onItemLongClick(View view, int position){

                                //TODO your own implementation on long item click


                            }

                        });

                        return viewHolder;
                    }
                };


        mRecyclerView.setAdapter(firebaseRecyclerAdapter);

    }

    @Override
    public void  onPause() {
        super.onPause();
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());

        View firstChild = mRecyclerView.getChildAt(0);
        int firstVisiblePosition = mRecyclerView.getChildAdapterPosition(firstChild);
        int offset = firstChild.getTop();

        Log.d(TAG, "Postition: " + firstVisiblePosition);
        Log.d(TAG, "Offset: " + offset);

        preferences.edit()
                .putInt("position", firstVisiblePosition)
                .putInt("offset", offset)
                .apply();
    }


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

    }
    @Override
    public void onSaveInstanceState(Bundle outState) {


        super.onSaveInstanceState(outState);
    }

    @Override
    public void onViewStateRestored(@Nullable Bundle savedInstanceState) {


        super.onViewStateRestored(savedInstanceState);
    }

}
How to&Answers:

After paying so many days, after trying many many solutions, finally found a working solution for my problem, thanks to https://stackoverflow.com/a/42563804/2128364

I’ve moved the working solution to another methods to make it work in the fragment.

Maybe this can save someone’s day, Here is how it worked:

  1. onConfigurationChanged (as suggested in original solution), onSaveInstanceState or onViewRestoredState methods doesn’t work, So you need onPause and onResume methods to save and restore the state respectively.
  2. You have to save the state in onPause method:

    mBundleRecyclerViewState = new Bundle();    
    
    mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
    
    mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);
    
  3. And then Restore it in onResume Method:

       if (mBundleRecyclerViewState != null) {
            new Handler().postDelayed(new Runnable() {
    
                @Override
                public void run() {
                    mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
                    mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);
    
                }
            }, 50);
        }
    
    
        mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
    

Answer:

You should use this method that returns the first item visible:

int findFirstVisibleItemPosition();

then you must save this index at onPause() and when user returns retrieve and set it at onResume():

void scrollToPosition (int position)