Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Diffutil in recycleview, making it autoscroll if a new item is added

If we use DiffUtil.Callback, and do

adapter.setItems(itemList);
diff.dispatchUpdatesTo(adapter);

how can we make sure that adding of new elements will scroll to that new position.

I have a case where I see item disappear, and a new one is created as a first element at the top, but not visible. It is hidden on top until you scroll down to make it visible. Before using DiffUtil, I was implementing this manually, and after I knew I was inserting at some position (on top) I could scroll to.

like image 572
miroslavign Avatar asked Apr 17 '17 19:04

miroslavign


3 Answers

There's an easy way to do this that also preserves the user's scroll position if items are inserted outside the viewable area:

import android.os.Parcelable;

Parcelable recyclerViewState = recyclerView.getLayoutManager().onSaveInstanceState();
// apply diff result here (dispatch updates to the adapter)
recyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);              

Using this approach, the new items are made visible if they are inserted where the user can see them -- but the user's viewpoint is preserved if the items are inserted outside of view.

like image 69
Lorne Laliberte Avatar answered Oct 20 '22 20:10

Lorne Laliberte


You have a dispatchUpdatesTo(ListUpdateCallback) method to use as well.

So you could just implement a ListUpdateCallback which gives you the first element inserted

class MyCallback implements ListUpdateCallback {
    int firstInsert = -1;
    Adapter adapter = null;
    void bind(Adapter adapter) {
        this.adapter = adapter;
    }
    public void onChanged(int position, int count, Object payload) {
        adapter.notifyItemRangeChanged(position, count, payload);
    }
    public void onInserted(int position, int count) {
        if (firstInsert == -1 || firstInsert > position) {
            firstInsert = position;
        }
        adapter.notifyItemRangeInserted(position, count);
    }
    public void onMoved(int fromPosition, int toPosition) {
        adapter.notifyItemMoved(fromPosition, toPosition);
    }
    public void onRemoved(int position, int count) {
        adapter.notifyItemRangeRemoved(position, count);
    }
}

and then just scroll the RecyclerView manually

myCallback.bind(adapter)
adapter.setItems(itemList);
diff.dispatchUpdatesTo(myCallback);
recycler.smoothScrollToPosition(myCallback.firstInsert);
like image 37
tynn Avatar answered Oct 20 '22 22:10

tynn


I used RecyclerView.AdapterDataObserver to detect when new items were added, and then scroll to the top of my list;

adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        super.onItemRangeInserted(positionStart, itemCount);
        recycleView.smoothScrollToPosition(0);
    }
});
like image 17
gregdev Avatar answered Oct 20 '22 20:10

gregdev