8

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.

Community
  • 1
  • 1
Copy33
  • 111
  • 1
  • 6
  • so you want to remove animation when item changes right? – Divyesh Patel Mar 08 '17 at 07:04
  • Instead of using notifyDataSetChanged(), use notifyItemRemoved(). NotifyDataSetChanged rebuilds the entire set of data in the RecyclerView, so all rows are redrawn. Whereas notifyItemRemoved() or notifyItemRangeChanged() will only refresh the items that were changed, so the jerky motion will dissappear. – Rachit Mar 08 '17 at 07:16
  • @Divyesh I don't want to remove the animation, I just want the whole page to animate nicely when I remove an item from the RecyclerView. By that I mean I want the page to wait for the RecyclerView item animation to be done before it translates. – Copy33 Mar 09 '17 at 19:25
  • @Rachit I am using notifyItemRemoved, I mentioned that clearly in my question. – Copy33 Mar 09 '17 at 19:26
  • @Rachit no, I think you misread it. All good though. Any suggestions on how to solve my problem? – Copy33 Mar 10 '17 at 06:21

4 Answers4

3

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

recyclerView.setHasFixedSize(true);
Mehmed Mert
  • 935
  • 1
  • 7
  • 24
1

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;
        }
    }
Divyesh Patel
  • 2,576
  • 2
  • 15
  • 30
  • That didn't work. The problem is not with the animation of the RecyclerView items themselves, the problem is how the page is handling all the other items below the RecyclerView ... the second i delete an item all the views below the RecyclerView snap to adapt to the new RecyclerView calculated height (after item deletion) BEFORE the remove animation of the item is finished. – Copy33 Mar 12 '17 at 05:25
1

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.

Mateo Kutnjak
  • 21
  • 1
  • 5
0

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

CaptainCrunch
  • 1,230
  • 14
  • 15