RecyclerView scroll to position when a new item is added
If want to add the data to the bottom of the list you need to use setStackFromEnd()
in RecyclerView Layout Manager.
But first, you need to fix your Adapter. You must not pass your RecylerView to your Adapter. So the following code is wrong:
...
// This is wrong!!
adapter = new RecyclerViewAdapter(data, recyclerView);
recyclerView.setAdapter(adapter);
You need to change your Adapter constructor to only receive the data as its parameter. Something like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Data> mData;
public RecyclerViewAdapter(List<Data> data) {
this.mData = data;
}
...
}
Then you can set the data to always added at the last bottom with the following code:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
recyclerView.setLayoutManager(linearLayoutManager);
adapter = new RecyclerViewAdapter(data);
recyclerView.setAdapter(adapter);
To add the new data, you better to make a new method in the adapter. Something like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Data> mData;
...
public void addItem(Data datum) {
mData.add(datum);
notifyItemInserted(mData.size());
}
}
Whenever you have adding a new data, you need to scroll to the bottom with scrollToPosition
method. Something like this:
adapter.addItem(newData);
recyclerView.scrollToPosition(adapter.getItemCount() - 1);
Remember that you need to override getItemCount()
in your Adapter. It should be something like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private List<Data> mData;
public RecyclerViewAdapter(List<Data> data) {
this.mData = data;
}
// Return the total count of items
@Override
public int getItemCount() {
return mData.size();
}
...
}
Please be aware that I'm using a Data pojo as a sample here. You need to change it according to your data type.
This was the solution I came up with, which was good enough for the basics, and didn't require any custom adapters.
myAdapter.registerAdapterDataObserver(object :
RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
myRecyclerView.scrollToPosition(positionStart)
}
})
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
super.onItemRangeInserted(positionStart, itemCount)
val msgCount = adapter.getItemCount()
val lastVisiblePosition =
linearLayoutManager.findLastCompletelyVisibleItemPosition()
if (lastVisiblePosition == -1 || positionStart >= msgCount - 1 &&
lastVisiblePosition == positionStart - 1) {
recyclerView.scrollToPosition(positionStart)
} else {
recyclerView.scrollToPosition(adapter.getItemCount() - 1);
}
}
})