Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why ItemTouchHelper.Callback's onChildDraw will be called after clearView

Tags:

android

The thing is I'd like to perform an elevation on the item while it's being dragged in RecyclerView.

Based on what the documentation says, I am supposed to customize my dragged view's behaviors in ItemTouchHelper.Callback#onChildDraw because it says

If you would like to customize how your View's respond to user interactions, this is a good place to override.

Then I should clear my customizations in ItemTouchHelper.Callback#clearView because it says

Called by the ItemTouchHelper when the user interaction with an element is over and it also completed its animation.

But the problem is onChildDraw will always be called one more time after clearView.

So even if I cleared the customized behaviors in clearView, still it will be re-called in onChildDraw. Then what's the purpose of clearView?

Here is my code:

class MyItemTouchCallback extends ItemTouchHelper.Callback {

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP | ItemTouchHelper.DOWN);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        mAdapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
                            RecyclerView.ViewHolder viewHolder,
                            float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        // Set elevation for dragged itemView
        ViewCompat.setElevation(viewHolder.itemView, getResources().getDimension(R.dimen.common_elevation));
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        // Clear elevation for dragged itemView
        ViewCompat.setElevation(viewHolder.itemView, 0);
    }
}
like image 934
Ryan Hoo Avatar asked Oct 18 '22 18:10

Ryan Hoo


1 Answers

Of course we can solve this problem with a trick, it's easy to understand. Remember with a boolean variable viewBeingCleared when the dragged itemView is being cleared, then we can decide to call customized code in onChildDraw or not.

However this is not the best solution. We'd better know why and then we will know how, isn't it?

Trick solution:

class MyItemTouchCallback extends ItemTouchHelper.Callback {

    boolean viewBeingCleared;

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP | ItemTouchHelper.DOWN);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        mAdapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
                            RecyclerView.ViewHolder viewHolder,
                            float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        if (viewBeingCleared) {
            viewBeingCleared = false;
        } else {
            ViewCompat.setElevation(viewHolder.itemView, getResources().getDimension(R.dimen.common_elevation));
        }
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        ViewCompat.setElevation(viewHolder.itemView, 0);
        viewBeingCleared = true;
    }
}
like image 150
Ryan Hoo Avatar answered Oct 21 '22 11:10

Ryan Hoo