0

I'm trying to implement the feature "Swipe to delete" item in recycleview. But the deleted item is can not restore correctly when invoke UNDO action on snackbar.

This is error. https://i.stack.imgur.com/Az5kv.png

I have tried to implement onChildDraw in different ways, but it does not work

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

// backup of removed item
final String deletedItem = adapter.arrIgnoreNumber.get(viewHolder.getAdapterPosition());
final int deletedIndex = viewHolder.getAdapterPosition();


adapter.removeItem(viewHolder.getAdapterPosition());
// showing snack bar with Undo option
Snackbar snackbar = Snackbar.make(coordinatorLayout, "Removed from the list!", Snackbar.LENGTH_LONG);
snackbar.setAction("UNDO", new View.OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            adapter.restoreItem(deletedItem, deletedIndex);
                        }
                    });
                    snackbar.setActionTextColor(Color.YELLOW);
                    snackbar.show();
                }
}

@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Bitmap icon;
            Paint p = new Paint();
            if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){

                View itemView = viewHolder.itemView;
                float height = (float) itemView.getBottom() - (float) itemView.getTop();
                float width = height / 3;

                if(dX > 0){
                    p.setColor(Color.parseColor("#388E3C"));
                    RectF background = new RectF((float) itemView.getLeft(), (float) itemView.getTop(), dX,(float) itemView.getBottom());
                    c.drawRect(background,p);
                    icon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_delete);
                    RectF icon_dest = new RectF((float) itemView.getLeft() + width ,(float) itemView.getTop() + width,(float) itemView.getLeft()+ 2*width,(float)itemView.getBottom() - width);
                    c.drawBitmap(icon,null,icon_dest,p);
                } else {
                    p.setColor(Color.parseColor("#D32F2F"));
                    RectF background = new RectF((float) itemView.getRight() + dX, (float) itemView.getTop(),(float) itemView.getRight(), (float) itemView.getBottom());
                    c.drawRect(background,p);
                    icon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.ic_delete);
                    RectF icon_dest = new RectF((float) itemView.getRight() - 2*width ,(float) itemView.getTop() + width,(float) itemView.getRight() - width,(float)itemView.getBottom() - width);
                    c.drawBitmap(icon,null,icon_dest,p);
                }
            }
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        }

I expect the deleted item can restore in list

  • Update: I provide my adapter for more detail:
@NonNull
        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int i) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View view = inflater.inflate(R.layout.list_ignore_item, parent, false);
            return new ViewHolder(view);
        }

@Override
        public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
            final int tmpPos = position;
            viewHolder.phone.setText(arrIgnoreNumber.get(position));
            String name = arrIgnoreNumber.get(position);
            if (!name.isEmpty()) {
                viewHolder.phone.setVisibility(View.VISIBLE);
                viewHolder.name.setText(name);
            } else {
                viewHolder.phone.setVisibility(View.GONE);
                viewHolder.name.setText(arrIgnoreNumber.get(position));
            }
            viewHolder.icon.setImageBitmap(Utils.getBitmapByContactNumber(mContext, arrIgnoreNumber.get(position), false));

@Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public int getItemCount() {
            return arrIgnoreNumber.size();
        }

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

        public void restoreItem(String item, int position) {
            arrIgnoreNumber.add(position, item);
            notifyItemInserted(position);
        }
Huy Miracle
  • 1
  • 1
  • 2
  • please provide your adapter – Zain Jul 25 '19 at 04:13
  • are you calling appropriate notifydatachanged methods on the adapter to tell there is a item restored? – Raghunandan Jul 25 '19 at 04:57
  • @Raghunandan: I have used both notifyDataSetChanged() and notifyItemInserted(). It does not work – Huy Miracle Jul 25 '19 at 05:05
  • 1
    https://www.androidhive.info/2017/09/android-recyclerview-swipe-delete-undo-using-itemtouchhelper/ – Sumit Shukla Jul 25 '19 at 05:05
  • @HuyMiracle item is restored at the position. something is wrong with the drawing i guess. check this https://medium.com/@zackcosborn/step-by-step-recyclerview-swipe-to-delete-and-undo-7bbae1fce27e and the gist here https://gist.github.com/keinix/b1aa2417dbea9311a1207eddf8b9d47b?source=post_page--------------------------- – Raghunandan Jul 25 '19 at 05:17
  • @Raghunandan: yes, I also think like that, but still can not find correctly way to draw it. I will check it. – Huy Miracle Jul 25 '19 at 05:24
  • @HuyMiracle the android hive has complete example. Take a look at that. That should help – Raghunandan Jul 25 '19 at 05:28
  • @Raghunandan: Yeh, it works. Although androidhive's solution has shoter code and works, hard to understand than others :) – Huy Miracle Jul 25 '19 at 07:10

1 Answers1

0

The following solution works for me. Refer to https://www.androidhive.info/2017/09/android-recyclerview-swipe-delete-undo-using-itemtouchhelper/

  1. In ItemTouchHelper.SimpleCallback:
@Override
        public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            return makeMovementFlags(0, ItemTouchHelper.LEFT);
        }

        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            return false;
        }

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

        @Override
        public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
            if (viewHolder != null) {
                final View foregroundView = ((IgnoreCallAdapter.ViewHolder) viewHolder).foreground;
                getDefaultUIUtil().onSelected(foregroundView);
            }
        }

        @Override
        public void onChildDrawOver(@NonNull Canvas c, @NonNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            final View foregroundView = ((IgnoreCallAdapter.ViewHolder) viewHolder).foreground;
            getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
                    actionState, isCurrentlyActive);
        }

        @Override
        public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            final View foregroundView = ((IgnoreCallAdapter.ViewHolder) viewHolder).foreground;
            getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
                    actionState, isCurrentlyActive);
        }

        @Override
        public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
            final View foregroundView = ((IgnoreCallAdapter.ViewHolder) viewHolder).foreground;
            getDefaultUIUtil().clearView(foregroundView);
        }

        @Override
        public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
            return 0.7f;
        }

        @Override
        public int convertToAbsoluteDirection(int flags, int layoutDirection) {
            return super.convertToAbsoluteDirection(flags, layoutDirection);
        }
  1. In item layout xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/row_view_height_ignore_list"
    android:focusable="true"
    android:foreground="?android:attr/selectableItemBackground"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/ignore_item_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/ignore_item_bg">
    </RelativeLayout>


    <LinearLayout
        android:id="@+id/ignore_item_foreground"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/bg_ignore_item_foreground"
        android:orientation="horizontal"
        android:visibility="visible">
        </LinearLayout>

    </LinearLayout>

</FrameLayout>
Huy Miracle
  • 1
  • 1
  • 2