0

Recently switched from notifyDataSetChanged to notifyItemInserted/Changed/Removed to preserve animations.

I'm stuck in this kind of situation. My adapter list initially is:

A B C D 

I then merge A, B and C to insert a new E item in last position:

A E 

I thought I could notify index 1 (B) as removed, index 2 (C) as removed, index 3 (D) as removed and then the new index 1 (E) as inserted, but an exception coming from I don't know where says:

Inconsistency detected. Invalid view holder adapter positionViewHolder...

So I thought that maybe the problem was raised by notifying twice for index 1, so I changed it to notify index 1 (B) as changed to E, index 2 (C) as removed and index 3 (D) as removed, but the same exception was thrown, leaving me with no more options.

What should be the right approach in such a situation?

3
  • Have you tried DiffUtils? Commented May 9, 2017 at 15:01
  • It messes with the animations, like using notifyDataSetChanged. :( Commented May 9, 2017 at 15:29
  • It shouldnt, DiffUtils respects animations well. Anyway, if you can guarantee stable ids, you can use those with notifyDataSetChanged too Commented May 10, 2017 at 5:51

1 Answer 1

2

To not let question without answer in case someone else has the same problem

With Support library 24.2 Diffutils was released as a helpful class to calculate differences between two sets of items.

Sample code could look something like this (creating two lists and with press of button swapping the items in adapter). DiffUtil.DiffResult will handle all notify calls.

 final List<RecyclerObject> list = new ArrayList<>(); list.add(new RecyclerObject("A")); list.add(new RecyclerObject("B")); list.add(new RecyclerObject("C")); list.add(new RecyclerObject("D")); final TestAdapter adapter = new TestAdapter(list); recycler.setAdapter(adapter); final List<RecyclerObject> newList = new ArrayList<>(); newList.add(new RecyclerObject("A")); newList.add(new RecyclerObject("E")); final View button = findViewById(R.id.mainButton); button.setOnClickListener(v -> { adapter.setList(newList); DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(list, newList)); diffResult.dispatchUpdatesTo(adapter); }); 

Diffcallback is own created class, implementation could look something like this. (unnecessary code removed for better readability)

public class DiffCallback extends DiffUtil.Callback { DiffCallback(final List<RecyclerObject> list, final List<RecyclerObject> newList) { this.list = list; this.newList = newList; } @Override public int getOldListSize() { return list.size(); } @Override public int getNewListSize() { return newList.size(); } @Override public boolean areContentsTheSame(final int oldItemPosition, final int newItemPosition) { return list.get(oldItemPosition).title.equals(newList.get(newItemPosition).title); } } 

There is also one less fancy method : If we can guarantee that items backing the adapter are "stable" and same item will always have the same identificator, recycler.adapter allows for setHasStableIds option. With that, even using only notifyDatasetChanged (which normally kills animations) will correctly process them. Requires override of function getItemId()

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.