RecyclerView使用ItemTouchHelper实现横向滑动删除

版权声明:个人原创,欢迎转载。 https://blog.csdn.net/chuyangchangxi/article/details/86552840

一、目标

实现删除文件夹收藏功能。

  • 普通状态下,通过横向滑动,移除收藏
    在这里插入图片描述

  • 编辑状态下,点击删除图标,再次点击移除按钮,移除收藏
    在这里插入图片描述

二、体验地址

神马笔记最新版本:【神马笔记 版本1.3.0.apk

三、功能设计

我们需要在普通状态和编辑状态下,均可以移除文件夹收藏。

  • 普通状态下的移除过程
    1. 从右向左滑动,移除按钮从右侧进入;
    2. 继续滑动,移除按钮固定在右侧;
    3. 继续滑动距离超过0.8,移除按钮移动到左侧,同时自动向左侧滑动一段距离,提醒用户松开即可移除;
  • 编辑状态下的移除过程
    1. 点击左侧删除按钮;
    2. 移除按钮从右侧进入;
    3. 点击移除按钮,移除文件夹收藏

四、准备工作

1. ItemTouchHelper

参考《RecyclerView使用ItemTouchHelper实现拖拽排序》一文,介绍了如何使用ItemTouchHelper以及通过实现ItemTouchHelper.Callback实现侧滑删除功能。

2. SwipeActionHelper

因为ItemTouchHelper是对ViewHolderitemView设置translationX实现滑动。

这样会导致列表项控件整体移动,因此,需要做一些定制才能完成功能设计。

这里,使用了SwipeActionHelper来完成这个工作。

你可以在WhatsAndroid项目中找到这份代码。

GitHub项目地址:https://github.com/jicanghai37927/WhatsAndroid

  • SwipeActionHelper.Adapter
public interface Adapter {

    boolean isSwiped(SwipeActionHelper helper);

    Adapter getActive(SwipeActionHelper helper);

    int getDirection(SwipeActionHelper helper);

    View getSwipeView(SwipeActionHelper helper);

    List<View> getTouchable(SwipeActionHelper helper);

    void clear(SwipeActionHelper helper);

    void onBegin(SwipeActionHelper helper);

    void onMove(SwipeActionHelper helper, float deltaX);

    void onEnd(SwipeActionHelper helper, float velocityX);

    void onDraw(SwipeActionHelper helper, Canvas canvas);

    void onDrawOver(SwipeActionHelper helper, Canvas canvas);

    void onActionBegin(SwipeActionHelper helper, int action);

    void onActionEnd(SwipeActionHelper helper, int action);

    void onClear(SwipeActionHelper helper, int direction);
}

子类ViewHolder需要实现Adapter接口,完成SwipeActionHelperViewHolder之间的通信。

  • SwipeViewHolder
public abstract class SwipeViewHolder<T> extends BridgeViewHolder<T> implements SwipeActionHelper.Adapter, MarginDividerDecoration.Adapter {
    
}

提供了SwipeActionHelper.Adapter的实现。

  • SwipeHolder
public class SwipeHolder {

    View container;
    View swipeView;

    boolean isDrag;

    SwipeRunner activeRunner;
    ArrayList<SwipeRunner> list;

    Rect bounds = new Rect();
    SwipeActionHelper helper;
    
}

横向滑动的容器。

  • SwipeRunner

定义了具体的滑动功能,目前支持5种滑动模式。

SwipeRunner子类 滑动行为
DeleteRunner 支持右侧删除按钮
RightDeleteRunner 支持右侧删除及菜单
RightActionRunner 支持右侧菜单
SnapRightRunner 支持按钮吸附在右侧
SnapLeftRunner 支持按钮吸附在左侧

五、组合起来

1. 定义布局资源

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorWindowBackground">

    <FrameLayout
            android:id="@+id/btn_delete"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/holo_red_light"
            android:foreground="?selectableItemBackground">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center"
                android:layout_gravity="left"
                android:paddingLeft="24dp"
                android:paddingRight="24dp"
                android:text="移除"
                android:textStyle="bold"
                android:textColor="@android:color/white"
                android:textAppearance="@style/TextAppearance.AppCompat.Button"/>

    </FrameLayout>

    <LinearLayout
            android:id="@+id/swipe_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:minHeight="@dimen/entranceItemHeight"
            android:paddingLeft="?listPreferredItemPaddingLeft"
            android:paddingRight="?listPreferredItemPaddingRight"
            android:background="@color/colorWindowBackground"
            android:foreground="?selectableItemBackground"
            android:clipToPadding="false"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            android:layout_gravity="center">

        <ImageView
                android:id="@+id/btn_remove"
                android:layout_width="28dp"
                android:layout_height="28dp"
                android:src="@drawable/ic_remove_circle_white_24dp"
                android:tint="@android:color/holo_red_light"
                android:scaleType="centerInside"
                android:layout_gravity="left|center_vertical"
                android:visibility="gone"
                android:layout_marginRight="?listPreferredItemPaddingRight"/>

        <ImageView
                android:id="@+id/iv_icon"
                android:layout_marginRight="?listPreferredItemPaddingRight"
                android:layout_width="28dp"
                android:src="@drawable/ic_folder_white_24dp"
                android:tint="@color/colorFolder"
                android:layout_height="28dp"/>

        <TextView
                android:id="@+id/tv_name"
                android:textAppearance="@style/TextAppearance.AppCompat.Title"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:singleLine="true"
                android:layout_weight="1"/>

        <ImageView
                android:id="@+id/btn_drag"
                android:layout_width="28dp"
                android:layout_height="28dp"
                android:src="@drawable/ic_drag_handle_white_24dp"
                android:tint="@color/colorDrag"
                android:scaleType="centerInside"
                android:visibility="gone"
                android:layout_gravity="right|center_vertical"
                android:layout_marginLeft="?listPreferredItemPaddingLeft"/>

    </LinearLayout>

</FrameLayout>

注意移除按钮的定义方式,必须使用FrameLayout作为外层容器。

2. 创建SwipeActionHelper

this.swipeActionHelper = new SwipeActionHelper();
swipeActionHelper.setOnSwipeActionListener(new SwipeActionListener());

swipeActionHelper.attach(recyclerView);

3. 创建SwipeHolder

SwipeHolder swipeHolder = new SwipeHolder(parent.swipeActionHelper, view, view.findViewById(R.id.swipe_view));

{
    View btnAction = view.findViewById(R.id.btn_delete);
    btnAction.setOnClickListener(this::onDeleteClick);

    DeleteRunner r = new DeleteRunner();
    r.add(btnAction);

    swipeHolder.add(r);
}

this.setSwipeHolder(swipeHolder);

使用DeleteRunner来实现侧滑动能。

4. 创建ItemDecoration

public class RemoveDecoration extends RecyclerView.ItemDecoration {

    ArrayList<RemoveInfo> list;

    public RemoveDecoration(Context context) {
        this.list = new ArrayList<>();
    }

    public void add(Drawable d, long duration) {
        this.list.add(new RemoveInfo(d, System.currentTimeMillis() + duration));
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);

        {
            drawVertical(c, parent, state);
        }
    }

    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);

    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
    }

    void drawVertical(Canvas canvas, RecyclerView parent, RecyclerView.State state) {

        if (parent.getLayoutManager() == null) {
            return;
        }

        canvas.save();

        {
            final int left;
            final int right;

            //noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
            if (parent.getClipToPadding()) {
                left = parent.getPaddingLeft();
                right = parent.getWidth() - parent.getPaddingRight();
                canvas.clipRect(left, parent.getPaddingTop(), right,
                        parent.getHeight() - parent.getPaddingBottom());
            }
        }

        {
            long time = System.currentTimeMillis();
            for (int i = list.size() - 1; i >= 0; i--) {
                RemoveInfo info = list.get(i);
                if (time >= info.expire) {
                    list.remove(i);
                }
            }
        }

        {
            for (RemoveInfo info: list) {
                info.drawable.draw(canvas);
            }
        }

        canvas.restore();
    }

    /**
     *
     */
    private static class RemoveInfo {

        Drawable drawable;
        long expire;

        RemoveInfo(Drawable d, long time) {
            this.drawable = d;
            this.expire = time;
        }
    }
}

通过RemoveDecoration实现列表项被移除时的红色背景。

六、Finally

王维的诗句结束这篇文章。

~随意春芳歇~王孙自可留~

猜你喜欢

转载自blog.csdn.net/chuyangchangxi/article/details/86552840