Use ItemTouchHelper for Swipe-To-Dismiss with anot

2020-05-13 03:40发布

I have a RecyclerView and want to allow my users to use a swipe gesture to remove items from the list. But as known from other apps (e.g. Gmail), I want to show a delete icon behind it, so that my users know that swiping results in a remove. However, I can't find an obvious way to do that. The ItemTouchHelper uses the viewHolder.itemView, so it takes the whole row.

My code:

    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new
            ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
                @Override
                public boolean onMove(
                        final RecyclerView recyclerView,
                        final RecyclerView.ViewHolder viewHolder,
                        final RecyclerView.ViewHolder target) {
                    return false;
                }

                @Override
                public void onSwiped(
                        final RecyclerView.ViewHolder viewHolder,
                        final int swipeDir) {
                    adapter.remove(viewHolder.getAdapterPosition());
                }
            };

    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(
            simpleItemTouchCallback
    );
    itemTouchHelper.attachToRecyclerView(itemsRecyclerView);

    itemsRecyclerView.setLayoutManager(
            new LinearLayoutManager(getContext())
    );
    itemsRecyclerView.setAdapter(adapter);

Has anyone a glue if this is possible at all? The only thing I can imagine right now is to extend the ItemTouchHelper / copy the code, and instead of using viewHolder.itemView I take a view identified by an ID.

2条回答
够拽才男人
2楼-- · 2020-05-13 03:48

use the onChildDraw() method from ItemTouchHelper - I have it working with a bitmap and coloured background for swiping left and right with different colors and icons:

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

            if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {

                View itemView = viewHolder.itemView;

                Paint paint = new Paint();
                Bitmap bitmap;

                if (dX > 0) { // swiping right
                    paint.setColor(getResources().getColor(R.color.child_view_complete));
                    bitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_circle_complete);
                    float height = (itemView.getHeight() / 2) - (bitmap.getHeight() / 2);

                    c.drawRect((float) itemView.getLeft(), (float) itemView.getTop(), dX, (float) itemView.getBottom(), paint);
                    c.drawBitmap(bitmap, 96f, (float) itemView.getTop() + height, null);

                } else { // swiping left
                    paint.setColor(getResources().getColor(R.color.primaryColorAccent));
                    bitmap = BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_circle_bin);
                    float height = (itemView.getHeight() / 2) - (bitmap.getHeight() / 2);
                    float bitmapWidth = bitmap.getWidth();

                    c.drawRect((float) itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom(), paint);
                    c.drawBitmap(bitmap, ((float) itemView.getRight() - bitmapWidth) - 96f, (float) itemView.getTop() + height, null);
                }

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


            }
        }

96f can be replaced with whatever distance from the left/right side you'd like it to be. This isn't perfect as when I'm down to 2 items in the adapter and I remove a position the canvas does not disappear and remains until the adapter is set again - working on trying to resolve now - I'll update if I find a complete solution but for now this is what I think you're looking for.

查看更多
萌系小妹纸
3楼-- · 2020-05-13 03:55

I have done it with having the following layout structure for the recycler view item:

<FrameLayout
    background = dark>
    <AnyLayout with content
       android:id="@+id/removable">
    </AnyLayout>
<FrameLayout>

Then I use this view holder as base view holder in my adapter:

public class RemovableViewHolder extends RecyclerView.ViewHolder {
    private View mRemoveableView;

    public RemovableViewHolder(final View itemView) {
        super(itemView);

        mRemoveableView = itemView.findViewById(R.id.removable);
    }

    public View getSwipableView() {
        return mRemoveableView;
    }
}

In my ItemTouchHelper.Callback class I extend the following methods like that:

@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    if (viewHolder instanceof RemovableViewHolder) {
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(0, swipeFlags);
    } else
        return 0;
}

@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
    getDefaultUIUtil().clearView(((RemovableViewHolder) viewHolder).getSwipableView());
}

@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
    if (viewHolder != null) {
        getDefaultUIUtil().onSelected(((RemovableViewHolder) viewHolder).getSwipableView());
    }
}

public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
    getDefaultUIUtil().onDraw(c, recyclerView, ((RemovableViewHolder) viewHolder).getSwipableView(), dX, dY,    actionState, isCurrentlyActive);
}

public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
    getDefaultUIUtil().onDrawOver(c, recyclerView, ((RemovableViewHolder) viewHolder).getSwipableView(), dX, dY,    actionState, isCurrentlyActive);
}

With this approach you use one level in layouts more, but saves yourself troubles with drawing on Canvas. Also you may select other views inside the item, for example save one that was touched and return it and have children of an item also swipeable.

Attaching to the recycler view:

final ItemTouchHelper.Callback callback = new RemovableItemTouchHelperCallback(mAdapter);
final ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView)
查看更多
登录 后发表回答