Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
670 views
in Technique[技术] by (71.8m points)

android - RecyclerView ItemTouchHelper swipe remove animation

I've got a remove on swipe, that draws a background (much like the Inbox app), implemented by an ItemTouchHelper - by overriding the onChilDraw method and drawing a rectangle on the provided canvas:

    ItemTouchHelper mIth = new ItemTouchHelper(
        new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {

            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
                remove(viewHolder.getAdapterPosition());
            }

            public boolean onMove(RecyclerView recyclerview, RecyclerView.ViewHolder v, RecyclerView.ViewHolder target) {
                return false;
            }

            @Override
            public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {

                View itemView = viewHolder.itemView;

                Drawable d = ContextCompat.getDrawable(context, R.drawable.bg_swipe_item_right);
                d.setBounds(itemView.getLeft(), itemView.getTop(), (int) dX, itemView.getBottom());
                d.draw(c);

                super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            }
        });

The remove method called above is in the Adapter:

    public void remove(int position) {
       items.remove(position);
       notifyItemRemoved(position);
    }

The background draws out nicely, but when notifyItemRemoved is called (according to Mr. Debugger), the RecyclerView first deletes my pretty green background, and then pushes the two adjacent items together.

enter image description here enter image description here

I would like it to keep the background there while it does that (just like the Inbox app). Is there any way to do that?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I had the same issue and I didn't wanna introduce a new lib just to fix it. The RecyclerView is not deleting your pretty green background, it's just redrawing itself, and your ItemTouchHelper is not drawing anymore. Actually it's drawing but the dX is 0 and is drawing from the itemView.getLeft() (which is 0) to dX (which is 0) so you see nothing. And it's drawing too much, but I'll come back to it later.

Anyway back to the background while rows animate: I couldn't do it within ItemTouchHelper and onChildDraw. In the end I had to add another item decorator to do it. It goes along these lines:

public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
    if (parent.getItemAnimator().isRunning()) {
        // find first child with translationY > 0
        // draw from it's top to translationY whatever you want

        int top = 0;
        int bottom = 0;

        int childCount = parent.getLayoutManager().getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getLayoutManager().getChildAt(i);
            if (child.getTranslationY() != 0) {
                top = child.getTop();
                bottom = top + (int) child.getTranslationY();                    
                break;
            }
        }

        // draw whatever you want

        super.onDraw(c, parent, state);
    }
}

This code takes into account only rows animating up, but you should also consider rows coming down. That happens if you swipe delete the last row, rows above are gonna animate down to that space.

When I said your ItemTouchHelper is drawing too much what I meant was: Looks like ItemTouchHelper keeps ViewHolders of removed rows in case they need to be restored. It's also calling onChildDraw for those VHs in addition to the VH being swiped. Not sure about memory management implications of this behavior but I needed an additional check in the start of onChildDraw to avoid drawing for "fantom" rows.

if (viewHolder.getAdapterPosition() == -1) {
    return;
}

In your case it's drawing from left=0 to right=0 so you don't see anything but the overhead is there. If you start seeing previously swiped away rows drawing their backgrounds that is the reason.

EDIT: I had a go at this, see this blog post and this github repo.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...