ehehhh ehehhh - 3 months ago 42
Android Question

RecyclerView scrolling on insert

I am trying to use

RecyclerView
to create a chat application. I am using a
LinearLayoutManager
with
setReverseLayout(true)
.

When I am scrolled all the way to the bottom (which is the dataset start = newest message) and a new message is inserted into the dataset, the item appears at the bottom of the list as expected (the view is scrolled up to make room for the new item).

The problem I have is when I have scrolled up to see the older messages. When a new message is inserted to the beginning of the dataset, the view is scrolled up by approximately one message height, even though that message isn't even rendered since it's out of the viewport's range.

How can I keep the scrolling behavior, when the view is scrolled to the bottom, but disable it when I have scrolled to the older messages?

UPDATE:
I also made a small app, where this problem is reproduced:
https://github.com/ehehhh/RecyclerViewProblem

UPDATE 2: I committed the fix that worked to the repo I made as well.

Relevant code (hopefully):

compile 'com.android.support:recyclerview-v7:24.2.0'


XML:

<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingBottom="8dp"
android:paddingTop="8dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>


RecyclerView init code:

layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setReverseLayout(true);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setScrollContainer(true);
recyclerView.setLayoutAnimation(null);
recyclerView.setItemAnimator(null);
adapter = new ChatAdapter(...);
recyclerView.setAdapter(adapter);


Adapter:

public class ChatAdapter extends RecyclerView.Adapter<ChatViewHolder> {

private List<MessageWrapper> dataset;

public ChatAdapter(List<MessageWrapper> dataset, ...) {
this.dataset = dataset;
setHasStableIds(true);
}

...

@Override
public long getItemId(int position) {
return dataset.get(position).getId();
}

@Override
public int getItemCount() {
return dataset.size();
}

public void datasetChanged(List<MessageWrapper> dataset) {
this.dataset = dataset;
notifyDataSetChanged();
}
}


When a new item is added to the dataset, I just call the
datasetChanged
method in the adapter.

Answer

in Recycler view using notifyDataSetChanged is redundant if you know the items changed you can use

notifyItemInserted(position) 

in this particular case what worked was

 notifyItemInserted(0);

or

 notifyItemRangeInserted(positionStart, newItems.size() - 1)

this will only rebind the views in this range

check https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html#notifyItemInserted(int)