airbnb / epoxy

Epoxy is an Android library for building complex screens in a RecyclerView
https://goo.gl/eIK82p
Apache License 2.0
8.48k stars 731 forks source link

Item jumps during disappear animation when using setItemSpacingDp #1086

Open denis-bezrukov opened 3 years ago

denis-bezrukov commented 3 years ago

We use setItemSpacingDp for some grids in our app. When items removed, they jump like if there is no spacing added for them

How it looks: ![ezgif-4-3931abe96f16](https://user-images.githubusercontent.com/7599577/99156340-92590b80-26d9-11eb-9016-c759ecd2ffb6.gif)

it's reproducible with minimal changes on epoxy-sample. Here is a link to the branch with these changes.

Steps:

  1. install the sample from the branch
  2. add some items
  3. tap clear button

Actual result: items being removed jump to top before disappear animation. Expected result: items do not jump

I didn't investigate it a lot, but probably in this case recycler returns NO_POSITION for the removing view, so decorator doesnt' modify the bounds:

    int position = parent.getChildAdapterPosition(view);
    if (position == RecyclerView.NO_POSITION) {
      // View is not shown
      return;
    }

Is there any workaround for this? Thanks in advance!

elihart commented 3 years ago

Thanks for the clear report. I don't know any workaround off the top of my head, and we don't internally use this decorator, so this won't be a high priority fix for us, but I'm happy to approve any change you want to make.

I just took a brief look to see if I could find any workarounds - one hacky possibility is to keep a weak reference to each view and store the last spacing applied to it, then if the view doesn't have an adapter position used the last spacing we gave it instead as a fallback

denis-bezrukov commented 3 years ago

yeah, I was thinking about something similar, but instead of weak ref per view I've used android's View.setTag/View.getTag for keeping last known offsets.

elihart commented 3 years ago

Good idea, that seems like a better solution

LiGuo802 commented 2 years ago

You can get the old position from viewHolder object:

var posInAll = recyclerView.getChildAdapterPosition(item) if (posInAll == RecyclerView.NO_POSITION) { posInAll = recyclerView.getChildViewHolder(itemView).oldPosition }