Search code examples
androidfirebaseandroid-recyclerviewfirebase-realtime-databasefirebaseui

Firebase RecyclerView - Scroll on Animation When New Item Added


I am using the FirebaseRecyclerAdapter, documentation located here.

Essentially, I want to scroll to the top of the RecyclerView as items are added to Firebase. It is tricky since I am not actually initialising the adapter with an ArrayList, or any list, for that matter. Firebase handles all of the data management through the FirebaseRecyclerAdapter.

Here is my basic onCreateView():

 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    final View v = inflater.inflate(R.layout.fragment_new, container, false);
    Log.v("TAG", "ON CREATE CALLED FROM NEW");

    mRecyclerview = (RecyclerView) v.findViewById(R.id.list);
    mRecyclerview.setItemAnimator(new FadeInUpAnimator());


    mLayoutManager = new LinearLayoutManager(getContext());
    mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
    mLayoutManager.setReverseLayout(true);
    mLayoutManager.setStackFromEnd(true);

    mRecyclerview.setLayoutManager(mLayoutManager);

    return v;
}

And my adapter:

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

    mFireAdapter = new FirebaseRecyclerAdapter<Poll, PollHolder>(Poll.class, R.layout.latest_item, PollHolder.class, mBaseRef.child("Polls")) {
        @Override
        protected void populateViewHolder(PollHolder viewHolder, Poll model, int position) {
            mRecyclerview.scrollToPosition(position);
            viewHolder.mPollQuestion.setText(model.getQuestion());
            Picasso.with(getActivity().getApplicationContext())
                    .load(model.getImage_URL())
                    .fit()
                    .into(viewHolder.mPollImage);
            Log.v("TAG", model.getQuestion());
            Log.v("TAG", model.getImage_URL());
        }
    };

    mRecyclerview.setAdapter(mFireAdapter);
}

How would I scroll to the top of the RecylcerView as items are added? Note that I am calling:

mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);

since I need to reverse the order of the data in Firebase.


Solution

  • I was actually able to find in the Firebase UI documentation - it is necessary to call the .registerAdapterDataObserver() method:

      @Override
    public void onStart() {
        super.onStart();
    
        mFireAdapter = new FirebaseRecyclerAdapter<Poll, PollHolder>(Poll.class, R.layout.latest_item, PollHolder.class, mBaseRef.child("Polls")) {
            @Override
            protected void populateViewHolder(PollHolder viewHolder, Poll model, int position) {
                viewHolder.mPollQuestion.setText(model.getQuestion());
                Picasso.with(getActivity().getApplicationContext())
                        .load(model.getImage_URL())
                        .fit()
                        .into(viewHolder.mPollImage);
                Log.v("TAG", model.getQuestion());
                Log.v("TAG", model.getImage_URL());
            }
        };
    
        mRecyclerview.setAdapter(mFireAdapter);
    
        mFireAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                super.onItemRangeInserted(positionStart, itemCount);
                int friendlyMessageCount = mFireAdapter.getItemCount();
                int lastVisiblePosition = mLayoutManager.findLastCompletelyVisibleItemPosition();
    
                // If the recycler view is initially being loaded or the user is at the bottom of the list, scroll
                // to the bottom of the list to show the newly added message.
                if (lastVisiblePosition == -1 ||
                        (positionStart >= (friendlyMessageCount - 1) && lastVisiblePosition == (positionStart - 1))) {
                    mRecyclerview.scrollToPosition(positionStart);
                }
            }
        });
    }