Copy33
Copy33

Reputation: 111

Android RecyclerView remove item animation bug

I'm trying to implement a ToDo list that contains a list and some other views below it on the activity page.

I am using a LinearLayout for the entire page and a RecyclerView for the list, alongside other views below the RecyclerView (ImageView, Buttons...etc.)

Here's my final view hierarchy:

<LinearLayout>
    <TextView />
    <RecyclerView />
    <Button />
    <EditText />
    <ImageView />
</LinearLayout>

I have implemented the RecyclerView where I can add and remove items. I am using a LinearLayoutManager without specifying an ItemAnimator so DefaultItemAnimator is used.

Adding items to the list works as expected. My problem is that the page doesn't animate well when I remove an item from the RecyclerView (by removing it from the dataset first then using RecyclerViewAdapter.notifyItemRemoved).

What happens is that the entire page snaps first to adapt to the new RecyclerView height, and then the RecyclerView item remove animation completes, which makes the behavior of the page look weird since all the views below the RecyclerView snap up while the deleted item fades out but hasn't lost its height yet, then the remaining RecyclerView items (below the deleted item) scroll up, looking like they're sliding up from under a wall.

I tried to look for solutions on the web and couldn't find anything to solve my problem.

I have found this unanswered question describing the same problem. Please refer to it in case my explanation wasn't clear enough.

Anyone encountering the same issue? Any suggestions?

Thanks.

Upvotes: 8

Views: 3407

Answers (4)

CaptainCrunch
CaptainCrunch

Reputation: 1403

You need to set the height of your recyclerview or the containers that hold it to: "match_parent". Worked like a cahrm for me.

source: https://medium.com/mobile-app-development-publication/recyclerview-supported-wrap-content-not-quite-f04a942ce624

Upvotes: 0

Mateo Kutnjak
Mateo Kutnjak

Reputation: 21

Create subclass of RecyclerView and override onTouchEvent method like this:

@Override
public boolean onTouchEvent(MotionEvent e) {
    if (findChildViewUnder(e.getX(), e.getY()) == null) {
        return false;
    }
    return super.onTouchEvent(e);
}

Also, don't use wrap_content as RecyclerView height/width depending on your orientation.

This way RecyclerView has fixed size and does not cut off items while removing them. Manipulation of onTouchEvent method assures that part of RecyclerView without items does not consumes click events and sends them to parent view of RecyclerView.

Upvotes: 1

Mehmed Mert
Mehmed Mert

Reputation: 1103

I had the same problem. In my situation just this line helped:

recyclerView.setHasFixedSize(true);

Upvotes: 3

Divyesh Patel
Divyesh Patel

Reputation: 2576

Try this one and check if it is working as your expectation:

recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(new MyAnim());


public static class MyAnim extends RecyclerView.ItemAnimator {
        @Override
        public boolean animateDisappearance(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
            return false;
        }

        @Override
        public boolean animateAppearance(@NonNull RecyclerView.ViewHolder viewHolder, @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
            return false;
        }

        @Override
        public boolean animatePersistence(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
            return false;
        }

        @Override
        public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
            final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);

            ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);

            ViewCompat.setAlpha(newHolder.itemView, 0);

            return true;
        }

        @Override
        public void runPendingAnimations() {

        }

        @Override
        public void endAnimation(RecyclerView.ViewHolder item) {

        }

        @Override
        public void endAnimations() {

        }

        @Override
        public boolean isRunning() {
            return false;
        }
    }

Upvotes: 1

Related Questions