【Android 事件分发】ItemTouchHelper 实现侧滑删除

举报
韩曙亮 发表于 2022/01/13 00:19:50 2022/01/13
【摘要】 Android 事件分发 系列文章目录 【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) 【And...

Android 事件分发 系列文章目录


【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )
【Android 事件分发】事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 一 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 二 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 三 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 四 | View 事件传递机制 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )
【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

【Android 事件分发】ItemTouchHelper 简介 ( 拖动/滑动事件 | ItemTouchHelper.Callback 回调 )
【Android 事件分发】ItemTouchHelper 实现侧滑删除 ( 设置滑动方向 | 启用滑动操作 | 滑动距离判定 | 滑动速度判定 | 设置动画时间 | 设置侧滑触发操作 )
【Android 事件分发】ItemTouchHelper 实现拖动排序 ( 设置滑动方向 | 启启用长按拖动功能 | 拖动距离判定 | 设置拖动触发操作 )






一、ItemTouchHelper.Callback 配置侧滑删除




1、设置移动标志 ( 拖动/滑动 )


重写 ItemTouchHelper.CallbackgetMovementFlags 方法 , 在该方法中设置滑动/拖动标志位 ;

滑动 / 拖动 标志 , 可使用 ItemTouchHelper.UP , ItemTouchHelper.DOWN , ItemTouchHelper.LEFT , ItemTouchHelper.RIGHT , 进行或操作得到 ;

        // 设置拖动方向, 此处设置上下拖动事件
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        // 设置滑动方向, 此处设置左右侧滑事件
        int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;

  
 
  • 1
  • 2
  • 3
  • 4

再将 拖动标志 和 滑动标志 传入 makeMovementFlags 方法 , 得到一个移动标志位 , 作为 getMovementFlags 方法的返回值 ;

makeMovementFlags(dragFlags, swipeFlags);

  
 
  • 1

public class Callback extends ItemTouchHelper.Callback {
    /**
     * 设置上下左右动作
     * 只有在此处打开了指定方向的设置 , 才可以应用具体方向的拖动
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView,
                                @NonNull RecyclerView.ViewHolder viewHolder) {
        // 设置拖动方向, 此处设置上下拖动事件
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        // 设置滑动方向, 此处设置左右侧滑事件
        int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        // 应用 拖动 和 滑动 设置
        return makeMovementFlags(dragFlags, swipeFlags);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

2、启用滑动动作


重写 ItemTouchHelper.CallbackisItemViewSwipeEnabled 方法 , 将该方法返回值设置为 true , 启用滑动操作 ;

public class Callback extends ItemTouchHelper.Callback {
    /**
     * 是否启用滑动操作
     * @return 是否启用 true 启用, false 不启用
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3、滑动距离判定设置


重写 ItemTouchHelper.CallbackgetSwipeThreshold 方法 , 设置用户的滑动距离 , 设置的是比例值, 返回值为 0.5 , 就意味着滑动宽度/高度的一半, 才触发侧滑 onSwiped 方法 ;

public class Callback extends ItemTouchHelper.Callback {
    /**
     * 用户滑动距离, 设置的是比例值, 返回值为 0.5 , 就意味着滑动宽度/高度的一半, 才触发侧滑 onSwiped 方法
     * @param viewHolder
     * @return
     */
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return 0.5f;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

上面案例中设置的滑动幅度是 0.5f , 在 getMovementFlags 方法中设置的滑动方向是左右滑动 , 因此在该案例中 , 滑动幅度在水平方向上超过 0.5 倍的条目组件宽度 , 侧滑删除才能生效 ;

下面的操作中 , 滑动在水平方向上没有超过组件宽度的 0.5 倍 , 滑动删除不生效 ;

在这里插入图片描述

下面的操作中 , 水平方向的滑动幅度超过了 0.5 倍 , 侧滑删除生效 ;

在这里插入图片描述

侧滑判定 : 这里有两种侧滑判定条件 , 满足任意一个即可触发侧滑删除 ;
① 条件一 : getSwipeThreshold 方法中设置的滑动距离 , 滑动超过 0.5 滑动幅度 , 触发侧滑删除 ;
② 条件二 : getSwipeEscapeVelocity 方法中设置的滑动速度 , 水平方向上每秒超过 5 像素的速度 , 触发侧滑删除 ;


4、滑动速度判定设置


重写 ItemTouchHelper.CallbackgetSwipeEscapeVelocity 方法 , 设置用户的滑动判定速度 , 单位是每秒移动的像素个数 , 达到该速度后 , 才可以被判定为滑动 ;

public class Callback extends ItemTouchHelper.Callback {
    /**
     * 滑动判定速度, 每秒移动的像素个数, 达到该速度后, 才可以被判定为滑动
     * @param defaultValue
     * @return
     */
    @Override
    public float getSwipeEscapeVelocity(float defaultValue) {
        return 5f;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

侧滑判定 : 这里有两种侧滑判定条件 , 满足任意一个即可触发侧滑删除 ;
① 条件一 : getSwipeThreshold 方法中设置的滑动距离 , 滑动超过 0.5 滑动幅度 , 触发侧滑删除 ;
② 条件二 : getSwipeEscapeVelocity 方法中设置的滑动速度 , 水平方向上每秒超过 5 像素的速度 , 触发侧滑删除 ;


5、设置动画时间


重写 ItemTouchHelper.CallbackgetAnimationDuration 方法 , 设置用户的手指离开后的动画持续时间 , 单位 毫秒 ms ;

public class Callback extends ItemTouchHelper.Callback {
    /**
     * 手指离开后的动画持续时间
     * @param recyclerView
     * @param animationType
     * @param animateDx
     * @param animateDy
     * @return
     */
    @Override
    public long getAnimationDuration(@NonNull RecyclerView recyclerView,
                                     int animationType,
                                     float animateDx, float animateDy) {
        return 200L;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

6、设置侧滑删除触发操作


重写 ItemTouchHelper.CallbackonSwiped 方法 , 用户侧滑判定成功后 , 会调用该方法 , 如果侧滑判定不成功 , 则不会调用该方法 ;

侧滑判定 : 这里有两种侧滑判定条件 , 满足任意一个即可触发侧滑删除 ;
① 条件一 : getSwipeThreshold 方法中设置的滑动距离 , 滑动超过 0.5 滑动幅度 , 触发侧滑删除 ;
② 条件二 : getSwipeEscapeVelocity 方法中设置的滑动速度 , 水平方向上每秒超过 5 像素的速度 , 触发侧滑删除 ;
可以只设置一个 , 也可以都设置 ;


public class Callback extends ItemTouchHelper.Callback {
    /**
     * 滑动时的回调操作
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        Log.i(TAG, "触发侧滑删除条目");
        // 滑动指定的距离, 达到一定幅度后, 就会触发该方法回调
        // 这里做的是滑动删除功能, 直接删除滑动项
        // 该方法中删除指定条目, 并刷新界面
        mAdapter.deleteItem(viewHolder.getAdapterPosition());
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

7、RecyclerView.Adapter 适配器中的删除操作


删除数据列表中的元素 , 并调用 notifyItemRemoved 触发删除动画 ;

    public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
        /**
         * 删除元素调用的方法
         * @param position
         */
        public void deleteItem(int position) {
            names.remove(position);
            notifyItemRemoved(position);
        }
	}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10




三、完整代码实现




1、主界面


package kim.hsl.recyclerview;

import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    /**
     * 数据源
     */
    private ArrayList<String> names = new ArrayList<String>();

    /**
     * 当前的 RecyclerView 列表
     */
    private RecyclerView recycler_view;

    /**
     * 布局管理器
     */
    private LinearLayoutManager layoutManager;

    /**
     * 适配器
     */
    private Adapter adapter;

    /**
     * 添加拖动处理
     */
    private ItemTouchHelper mItemTouchHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化数据
        initData();

        //1 . 从布局中获取 RecyclerView
        recycler_view = findViewById(R.id.recycler_view);

        //2 . 创建并设置布局管理器
        //创建布局管理器
        layoutManager = new LinearLayoutManager(
                this,
                RecyclerView.VERTICAL,
                false);

        //设置布局管理器
        recycler_view.setLayoutManager(layoutManager);

        // 设置边距
        recycler_view.addItemDecoration(new ItemDecoration());

        //3 . 创建并设置列表适配器
        adapter = new Adapter();
        recycler_view.setAdapter(adapter);

        //4. 添加拖动事件
        Callback callback = new Callback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(recycler_view);
    }

    /**
     * 初始化数据
     */
    private void initData(){
        names.add("宋江");
        names.add("卢俊义");
        names.add("吴用");
        names.add("公孙胜");
        names.add("关胜");
        names.add("林冲");
        names.add("秦明");
        names.add("呼延灼");
        names.add("花荣");
        names.add("柴进");
        names.add("李应");
        names.add("朱仝");
        names.add("鲁智深");
        names.add("武松");
        names.add("董平");
        names.add("张清");
        names.add("杨志");
        names.add("徐宁");
        names.add("索超");
    }

    /**
     * RecyclerView 适配器
     */
    public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {

        private RecyclerView mRecyclerView;

        @Override
        public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
            this.mRecyclerView = recyclerView;
        }

        @Override
        public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View root_view = LayoutInflater.from(MainActivity.this)
                    .inflate(R.layout.item_recyclerview, parent, false);
            return new ViewHolder(root_view);
        }

        @Override
        public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
            holder.text.setText("" + names.get(position));
        }

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

        public class ViewHolder extends RecyclerView.ViewHolder {
            TextView text;
            public ViewHolder(@NonNull View itemView) {
                super(itemView);
                text = itemView.findViewById(R.id.text);
            }
        }

        /**
         * 删除元素调用的方法
         * @param position
         */
        public void deleteItem(int position) {
            names.remove(position);
            notifyItemRemoved(position);
        }
    }

}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155

2、ItemTouchHelper.Callback 回调类


package kim.hsl.recyclerview;

import android.util.Log;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

public class Callback extends ItemTouchHelper.Callback {

    private static final String TAG = "Callback";

    private MainActivity.Adapter mAdapter;

    public Callback(MainActivity.Adapter mAdapter) {
        this.mAdapter = mAdapter;
    }

    /**
     * 设置上下左右动作
     * 只有在此处打开了指定方向的设置 , 才可以应用具体方向的拖动
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView,
                                @NonNull RecyclerView.ViewHolder viewHolder) {
        // 设置拖动方向, 此处设置上下拖动事件
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        // 设置滑动方向, 此处设置左右侧滑事件
        int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        // 应用 拖动 和 滑动 设置
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /*
        以下是拖动相关方法
     */

    /**
     * 是否启用长按拖动功能
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * 拖动幅度设置
     * 组件在宽度 / 高度 上移动超过该比例 , 就认为拖动触发, 执行拖动相关操作
     * @param viewHolder
     * @return
     */
    @Override
    public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        // 该案例中, 拖动操作只能上下进行
        // 拖动超过条目组件高度超过 0.9 倍, 即可触发拖动操作
        return 0.9f;
    }

    /**
     * 监听滑动事件
     * 滑动分 水平 / 垂直 两个方向
     * @param recyclerView
     * @param viewHolder
     * @param target
     * @return
     */
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder viewHolder,
                          @NonNull RecyclerView.ViewHolder target) {
        // 拖动后交换数据, 该方法中交换 Adapter 中的数据, 并刷新界面
        Log.i(TAG, "触发拖动交换条目");
        return true;
    }

    /*
        以下是滑动相关方法
     */

    /**
     * 是否启用滑动操作
     * @return 是否启用 true 启用, false 不启用
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    /**
     * 用户滑动距离, 设置的是比例值, 返回值为 0.5 , 就意味着滑动宽度/高度的一半, 才触发侧滑 onSwiped 方法
     * @param viewHolder
     * @return
     */
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return 0.5f;
    }

    /**
     * 滑动判定速度, 每秒移动的像素个数, 达到该速度后, 才可以被判定为滑动
     * @param defaultValue
     * @return
     */
    @Override
    public float getSwipeEscapeVelocity(float defaultValue) {
        return 5000f;
    }

    /**
     * 手指离开后的动画持续时间
     * @param recyclerView
     * @param animationType
     * @param animateDx
     * @param animateDy
     * @return
     */
    @Override
    public long getAnimationDuration(@NonNull RecyclerView recyclerView,
                                     int animationType,
                                     float animateDx, float animateDy) {
        return 200L;
    }

    /**
     * 滑动时的回调操作
     * @param viewHolder
     * @param direction
     */
    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
        Log.i(TAG, "触发侧滑删除条目");
        // 滑动指定的距离, 达到一定幅度后, 就会触发该方法回调
        // 这里做的是滑动删除功能, 直接删除滑动项
        // 该方法中删除指定条目, 并刷新界面
        mAdapter.deleteItem(viewHolder.getAdapterPosition());
    }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142

3、执行效果


在这里插入图片描述





三、博客资源



博客资源 :

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/118581682

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。