Ehsan
Ehsan

Reputation: 2781

RecyclerView position will changed by receiving new data

I used notifyDataSetChanged in in my recycleriew. like this:

 private void setList(List<Article> articles) {

    mainList.addAll(articles);
    notifyDataSetChanged();
  }

But I want to use diffUtill in my recycleriew. I created my own diffUtill like this:

public class ArticleListDiffTool extends DiffUtil.Callback {

  List<Article> oldList;
  List<Article> newList;

  private static final String TAG = "ArticleListDiffTool";
  public ArticleListDiffTool(List<Article> oldList, List<Article> newList) {

    this.oldList = oldList;
    this.newList = newList;

    Log.d(TAG, "ArticleListDiffTool: " + this.oldList.size() + "\n" + this.newList.size());
  }

  @Override
  public int getOldListSize() {
    return oldList.size();
  }

  @Override
  public int getNewListSize() {
    return newList.size();
  }

  @Override
  public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
    return oldList.get(oldItemPosition).getId() .equals( newList.get(newItemPosition).getId());
  }

  @Override
  public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
    return oldList.get(oldItemPosition).equals(newList.get(newItemPosition));
  }

  @Nullable
  @Override
  public Object getChangePayload(int oldItemPosition, int newItemPosition) {
    //you can return particular field for changed item.
    return super.getChangePayload(oldItemPosition, newItemPosition);
  }
}

And I use it in my adapter :

private void setList(List articles) {

    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ArticleListDiffTool(this.mainList, articles),true);
    mainList.addAll(articles);
    diffResult.dispatchUpdatesTo(this);
  }

I want to add new data to my old list. But when new data received, the recyclerView will be scrolled to the top of the list. But I want to recyclerView be in its user state and new data add to the rest of the old list.

Upvotes: 1

Views: 1719

Answers (3)

Ehsan
Ehsan

Reputation: 2781

I Found the problem after a few hours.

I every time that wants to update my recyclerView send received data to my adapter And DiffUtil goes to compare my old list with received new parts and because of that (completely new items) DiffUtil decides to refresh whole recyclerView list.

Solution: Now I get the current list from the recyclerView adapter and use addAll to insert new items that received from the server, then I pass this complete list to the adapter.

Now DiffUtil can compare diffrent between my old and new lists and recyclerView will be stay at it's current position.

Upvotes: 1

David Medenjak
David Medenjak

Reputation: 34542

RecylcerView updates knows nothing about your views. When you call notifyDataSetChanged it tries to determine which views moved, or were replaced. I don't see you using setHasStableIds so when calling notifyDataSetChanged it will assume all of the content was replaced. It will jump to position 0 and be done with it. When you use setHasStableIds it will check the ids of the visible items and update the content in them. It will stop jumping around.

Now you also show that you are using DiffUtil. This is great! When you're not working with setHasStableIds this is the way to properly tell the recyclerView about what changed.

The problem you are facing is that you're using both. Either move to long ids and let the recyclerview do the diffing itself, or use DiffUtil and remove the call to notifyDataSetChanged. Either variant should work.

Upvotes: 4

user8959091
user8959091

Reputation:

If you add new items at the end of the recyclerview, before you add the data, store the 1st visible item in recyclerview:

int position = ((LinearLayoutManager) recyclerView.GetLayoutManager()).findFirstVisibleItemPosition();

and after you make all the changes and call notifyDatasetChanged() scroll to position:

recyclerView.scrollToPosition(position);

Upvotes: 1

Related Questions