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 be
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)
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.