Reputation: 2428
I'm using diff utils to update my recycler view (live data from view model returns a list), I have an item decoration that adds a large amount of padding to the last element in the list but as diff utils updates the recycler view properly by calling notifyItemInserted and not notifyDataSetChanged, the decoration is only applied to the last element ADDED so all my items end up with a large amount of padding on the end, if I add an item to my recycler view and call notify dataset changed the adapter rebuilds all the items and the padding is only on the last element (which is what I want) also if i add the same item twice it adds it before the last added for instance if i have a list of 1,2,3 and i add another 3 it gets added before the existing 3 so 1,2,3b,3a, is there any way to have a little more control over this?
Item decoration
public class PredictionsHorizontalSpaceCardDecoration extends RecyclerView.ItemDecoration {
private final int horizontalSpace;
private final int endSpace;
public PredictionsHorizontalSpaceCardDecoration(int horizontalSpace, int endSpace) {
this.horizontalSpace = horizontalSpace;
this.endSpace = endSpace;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
Log.d("Deco", "parent child count " + (parent.getChildCount() - 1) + " position " + parent.getChildAdapterPosition(view));
outRect.right = horizontalSpace;
outRect.top = horizontalSpace;
outRect.bottom = horizontalSpace;
outRect.left = horizontalSpace;
if (parent.getChildAdapterPosition(view) == parent.getChildCount() - 1){
outRect.right = endSpace;
}
}
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull State state) {
super.onDraw(c, parent, state);
}
}
Diff utils
public class MyDiffCallback extends DiffUtil.Callback{
List<Card> oldCards;
List<Card> newCards;
String TAG = "diffUtils";
public MyDiffCallback(List<Card> newCards, List<Card> oldCards) {
this.newCards = newCards;
this.oldCards = oldCards;
}
@Override
public int getOldListSize() {
return oldCards.size();
}
@Override
public int getNewListSize() {
return newCards.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
boolean areItemsTheSame = oldCards.get(oldItemPosition).getCardId() == newCards.get(newItemPosition).getCardId();
Log.d(TAG,"areItemsTheSame " + areItemsTheSame);
return oldCards.get(oldItemPosition).getCardId() == newCards.get(newItemPosition).getCardId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
boolean areItemsTheSame = oldCards.get(oldItemPosition).equals(newCards.get(newItemPosition));
Log.d(TAG,"areContentsTheSame " + areItemsTheSame);
return oldCards.get(oldItemPosition).equals(newCards.get(newItemPosition));
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
//you can return particular field for changed item.
Log.d(TAG,"getChangePayload ");
return super.getChangePayload(oldItemPosition, newItemPosition);
// return newCards.get(newItemPosition).getCardId();
}
}
updated from recycler view adapter
public class CalculateDiffUtils extends AsyncTask<Void, Void, DiffUtil.DiffResult> {
private List<Card> oldCardList;
private List<Card> newCardList;
CalculateDiffUtils(List<Card> oldCardList, List<Card> newCardList) {
this.oldCardList = oldCardList;
this.newCardList = newCardList;
}
@Override
protected DiffUtil.DiffResult doInBackground(Void... params) {
return DiffUtil.calculateDiff(new MyDiffCallback(newCardList, oldCardList));
}
@Override
protected void onPostExecute(DiffUtil.DiffResult diffResult) {
super.onPostExecute(diffResult);
dispatchUpdates(diffResult, newCardList);
}
}
public void dispatchUpdates(DiffUtil.DiffResult diffResult, List<Card> newCardList){
this.cardList.clear();
this.cardList.addAll(newCardList);
diffResult.dispatchUpdatesTo(this);
}
Upvotes: 5
Views: 970
Reputation: 161
I had a similar issue to what you are describing, and I found this searching for the answer.
I don't know if this detail is impacting you, but you are using parent.getChildCount() - 1
for the last item position. I was originally using the state.getItemCount()
(minus one), but that proved to be a problem since my list changes size. I ended up using the parent.getLayoutManager().getItemCount()
method (with the requisite null check and offset).
What I noticed, though, playing with adding and deleting items, and changing the list size, was the DiffUtil wouldn't update items that didn't need to be redrawn, so I actually did a check to see if the item that was changed was the last in my list and then called notifyItemChanged()
on the adjacent item. That would ensure the item decoration was applied correctly. I added this right after I made the call to diffResult.dispatchUpdatesTo(this)
i.e. notifyItemChanged(positionOfNewOrOldLastElement)
.
Upvotes: 2