Java – Unable to maintain Firestore RecyclerView state in back navigation

Unable to maintain Firestore RecyclerView state in back navigation… here is a solution to the problem.

Unable to maintain Firestore RecyclerView state in back navigation

I have a fragment that contains Recylcerview. This RecyclerView is populated using the Firebase FirestoreUI.

Below is the code for my RecyclerView, which I call in .onStart() to listen for changes:

@Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        if (savedInstanceState != null) {
            mRecyclerViewState = savedInstanceState.getParcelable(RECYCLERVIEW_STATE);
        }
        mRecyclerview = (RecyclerView) view.findViewById(R.id.list);
        mPollHolderArray = new ArrayList<>();
        mRecyclerview.getItemAnimator().setChangeDuration(0);

mLayoutManager = new LinearLayoutManager(getContext());
        mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        TODO: Detertmine if necessary since ordering is now different in Firebase
        mLayoutManager.setReverseLayout(true);
        mLayoutManager.setStackFromEnd(true);

mFloatingActionAdd = (FloatingActionButton) getActivity().findViewById(R.id.myFAB);
        mFloatingActionAdd.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent I = new Intent(getActivity().getApplicationContext(), CreateActivity.class);
                startActivity(I);
            }
        });

mRecyclerview.setLayoutManager(mLayoutManager);

if (mRecyclerViewState != null){
            mLayoutManager.onRestoreInstanceState(mRecyclerViewState);
        }

if (mFirestoreAdaper == null) {
            Query queryStore = FirebaseFirestore.getInstance()
                    .collection(POLLS_LABEL)
                    .orderBy("timestamp", Query.Direction.ASCENDING);
            Cloud Firestore does not have any ordering; must implement a timestampe to order sequentially

FirestoreRecyclerOptions<Poll> storeOptions = new FirestoreRecyclerOptions.Builder<Poll>()
                    .setQuery(queryStore, Poll.class)
                    .build();
            mFirestoreAdaper = new FirestoreRecyclerAdapter<Poll, PollHolder>(storeOptions) {
                @Override
                protected void onBindViewHolder(@NonNull final PollHolder holder, final int position, @NonNull Poll model) {
                    holder.mPollQuestion.setText(model.getQuestion());
                    String voteCount = String.valueOf(model.getVote_count());
                    TODO: Investigate formatting of vote count for thousands
                    holder.mVoteCount.setText(voteCount);
                    Picasso.get()
                            .load(model.getImage_URL())
                            .fit()
                            .into(holder.mPollImage);
                    holder.mView.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            Intent toClickedPoll = new Intent(getActivity(), PollHostActivity.class);
                            String recyclerPosition = getSnapshots().getSnapshot(position).getId();
                            Log.v("Firestore ID", recyclerPosition);
                            toClickedPoll.putExtra("POLL_ID", recyclerPosition);
                            startActivity(toClickedPoll);

}
                    });
                }

@Override
                public PollHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                    View v = LayoutInflater.from(parent.getContext())
                            .inflate(R.layout.latest_item, parent, false);
                    return new PollHolder(v);
                }

};
            mRecyclerview.setAdapter(mFirestoreAdaper);
        }
    }

When I navigate to another fragment (onClick for RecyclerView) and then return to the fragment that contains this RecyclerView, I want to be able to stay where I was clicked. However, this did not happen. Below is the code I tried to save the state, it just reverted to the top item of RecyclerView:

    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    mRecyclerViewState = mLayoutManager.onSaveInstanceState();
    outState.putParcelable(RECYCLERVIEW_STATE, mRecyclerViewState);

}

EDIT: The original fragment is part of ViewPager (I’m testing it on NewFragment right now):

    public class SectionPagerAdapter extends FragmentPagerAdapter {

public SectionPagerAdapter(FragmentManager fm) {
        super(fm);
    }

@Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return new TrendingFragment();
            case 1:
                return new FollowingFragment();
            case 2:
                return new NewFragment();
            default:
                return new TrendingFragment();
        }
    }

Solution

That’s because you set up a new adapter in onStart, which happens whenever you resume a fragment from a previous screen or background.

You don’t need all this logic to help remember the location, you just initialize and set up your adapter in the right place for the lifetime.

In your fragment, set up your adapter in onViewCreated instead of onStart:

@Override public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    mRecyclerview = view.findViewById(...);
    if (mAdapter == null) {
        mAdapter = new FirestoreRecyclerAdapter(...)
        mRecyclerView.setAdapter(mAdapter);
        mAdapter.startListening();
    }
}

@Override public void onDestroyView() {
    super.onDestroyView();
    mAdapter.stopListening();
}

And remove all the logic for saving and restoring locations (onSaveInstanceState, scrollToPosition, onRestoreInstanceState), you don’t need them!

Related Problems and Solutions