RecyclerView 使用ItemTouchHelper实现拖动和侧滑删除

您所在的位置:网站首页 onchilddraw RecyclerView 使用ItemTouchHelper实现拖动和侧滑删除

RecyclerView 使用ItemTouchHelper实现拖动和侧滑删除

2023-10-08 10:57| 来源: 网络整理| 查看: 265

ItemTouchHelper的核心在于ItemTouchHelper.Callback接口。temTouchHelper.Callback中几个比较重要的方法:

getSwipeThreshold() (返回值表示所能够接受的侧滑滑动的最大距离,当我们侧滑的距离大于该方法的返回值时,就会触发侧滑删除的操作。如果是自定义了删除按钮进行点击来执行删除item操作,因为该返回值表示百分比,那么我们只要把值设置成大于1就可以使得无论侧滑多少都不会触发侧滑删除这个操作了) getSwipeEscapeVelocity() (侧滑的速度大于该方法的返回值也会触发侧滑删除的操作,如果是自定义删除按钮,设置值同上) onChildDraw() (该方法在ItemView进行滑动时会回调,这里的滑动包括:1、手指滑动;2、ItemView的位移动画。可以根据isCurrentlyActive参数来判断是手指滑动还是动画滑动) clearView() (该方法在ItemView滑动完成之后会回调,想要实现侧滑ItemView停在某种状态,判断手指松开时ItemView的滑动距离是否大于我们设置的阈值(是否显示删除按钮的阈值),如果大于的话,只滑动到指定的位置,如果小于的话,就回到初始位置)

具体实现: 1、创建一个Activity,RecyclerActivity.java:

//import android.graphics.Canvas; import android.os.Bundle; import android.view.View; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; //import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; public class RecyclerActivity extends FatherActivity{ private String[] titles = {"china","america","apple","canada","water","flower","candy","lisa","jon","teacher","student","moon","second","dog"}; private String[] contents = {"This is china","This is america","This is apple","This is canada","This is water","This is water","This is water","This is water","This is water","This is water","This is water","This is water","This is water","This is water"}; private int[] leftImages = {R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher,R.mipmap.ic_launcher}; List list;//数据源 RecyclerView recyclerView; CustomRecyclerViewAdapter customRecyclerViewAdapter; // RecyclerModel recyclerModel;//保存正在拖动的item所对应的对象 // String dragStartTag;//记录recyclerview拖动item的起始位置 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_recycler); initView(); } void initView(){ //加载数据 list = new ArrayList(); for(int i = 0; i < titles.length; i++){ RecyclerModel model = new RecyclerModel(leftImages[i],titles[i],contents[i]); list.add(model); } customRecyclerViewAdapter = new CustomRecyclerViewAdapter(list); recyclerView = findViewById(R.id.recycler_recyclerview); //设置adapter recyclerView.setAdapter(customRecyclerViewAdapter); //加载LayoutManager LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(linearLayoutManager); //初始化ItemTouchHelper ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new CustomItemTouchHelperCallback(GeneralFunction.dpToPx(this, 60),list)); itemTouchHelper.attachToRecyclerView(recyclerView); // //拖动起始记录值与拖动的item暂存对象初始值为空 // recyclerModel = null; // dragStartTag = null; // ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() { // //itemView可滑动的最大阈值(当滑动距离超过最大阈值,松开手指时会回弹到阈值的位置) // int mDefaultScrollX = GeneralFunction.dpToPx(RecyclerActivity.this,64); // /** // *这个方法用于让RecyclerView拦截上下滑动和左右滑动的事件 // * makeMovementFlags(dragFlags, swipeFlags);dragFlags是上下方向的滑动 swipeFlags是左右方向上的滑动 // */ // @Override // public int getMovementFlags(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) { // return makeMovementFlags(ItemTouchHelper.UP|ItemTouchHelper.DOWN,ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT); // } // // /** // * 针对drag状态,在canDropOver()返回true时,会调用该方法 // * 这里需要我们自己处理拖动后互换位置的逻辑 // */ // @Override // public boolean onMove(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder, @NotNull RecyclerView.ViewHolder target) { // if(recyclerView.getAdapter() != null){//拖动过程中不断更换位置 // recyclerView.getAdapter().notifyItemMoved(viewHolder.getAdapterPosition(),target.getAdapterPosition()); // } // return true; // } // // /** // * 针对drag状态,当前target对应的item是否允许移动 // * 我们一般用drag来做一些换位置的操作,就是当前对应的target对应的Item可以移动 // */ // @Override // public boolean canDropOver(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder current, @NotNull RecyclerView.ViewHolder target) { // return true; // } // // /** // * 针对drag状态,当drag ItemView跟底下ItemView重叠时,可以给drag ItemView设置一个Margin值 // * 让重叠不容易发生,相当于增大了drag Item的区域 // */ // @Override // public int getBoundingBoxMargin() { // return 0; // } // // /** // * 针对drag状态,当滑动超过多少就可以出发onMove()方法(这里指onMove()方法的调用,并不是随手指移动的View) // */ // public float getMoveThreshold(@NotNull RecyclerView.ViewHolder viewHolder) { // return .5f; // } // // /** // * 针对drag状态,在drag的过程中获取drag itemView底下对应的ViewHolder(一般不用我们处理直接super就好了) // */ // public RecyclerView.ViewHolder chooseDropTarget(@NotNull RecyclerView.ViewHolder selected, // @NotNull List dropTargets, // int curX, // int curY) { // return super.chooseDropTarget(selected, dropTargets, curX, curY); // } // // /** // * 当onMove return true的时候调用(一般不用我们自己处理,直接super就好) // */ // public void onMoved(@NotNull final RecyclerView recyclerView, // @NotNull final RecyclerView.ViewHolder viewHolder, // int fromPos, // @NotNull final RecyclerView.ViewHolder target, // int toPos, // int x, // int y) { // super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y); // } // // /** // * 针对swipe和drag状态,当一个item view在swipe、drag状态结束的时候调用 // * drag状态:当手指释放的时候会调用 // * swipe状态:当item从RecyclerView中删除的时候调用,一般我们会在onSwiped()函数里面删除掉指定的item view // */ // public void clearView(@NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder) { // super.clearView(recyclerView, viewHolder); // // if (recyclerView.getAdapter()!=null){ // //dragStartTag和recyclerModel都不为空则是drag状态 // if (dragStartTag!=null && recyclerModel!=null){ // viewHolder.itemView.setBackgroundColor(getResources().getColor(R.color.colorSelect));//item样式恢复原样 // int endLocation = viewHolder.getAdapterPosition();//获取正在拖动的item对象的最终目标位置 // int start = Integer.parseInt(dragStartTag);//获取已记录的item起始位置 // list.remove(start);//先删除数组里相对应的正在拖动的item对象 // list.add(endLocation,recyclerModel);//在数组对应的最终目标位置插入先前保存的item对象 // if (start>endLocation){ // //起始位置大于目标位置,那么recyclerView局部刷新范围则是,从目标位置开始到起始位置结束范围内刷新数据 // recyclerView.getAdapter().notifyItemRangeChanged(endLocation,start-endLocation+1); // }else if (start= mDefaultScrollX) { //// viewHolder.itemView.scrollTo(mDefaultScrollX, 0);//让item最终偏移量为阈值即可显示删除按钮 //// } else { //// //往右滑的时候直接恢复初始位置(此时getScrollX()永远小于阈值,因为往右滑偏移量是负值) //// viewHolder.itemView.scrollTo(0, 0); //// } //// } // } // // } // // /** // * 针对swipe和drag状态,整个过程中一直会调用这个函数,随手指移动的view就是在super里面做到的(和ItemDecoration里面的onDraw()函数对应) // */ // public void onChildDraw(@NotNull Canvas c, @NotNull RecyclerView recyclerView, @NotNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { // super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); // // //当处于Swipe状态时 // if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){ // //dX的值,表示手指滑动的距离,往右滑动为正,往左为负 // // getScrollX()的值,表示itemView的偏移量,往右是负,往左是正 // if (isCurrentlyActive) {//手指滑动时 // // 基于当前位置的偏移 // viewHolder.itemView.scrollTo( (int)-dX, 0); // } else {//手指松开时,因为我们设置了侧滑距离无论多大也不会删除item,所以手指松开时,itemView会回滚 // //判断ItemView的偏移量是否大于给定阈值,如果大于让itemView的偏移量最终停留在阈值,显示删除按钮 // if (viewHolder.itemView.getScrollX() >= mDefaultScrollX) { // viewHolder.itemView.scrollTo(mDefaultScrollX, 0);//让item最终偏移量为阈值即可显示删除按钮 // } else { // //往右滑的时候直接恢复初始位置(此时getScrollX()永远小于阈值,因为往右滑偏移量是负值) // viewHolder.itemView.scrollTo(0, 0); // } // } // } // // } // // /** // * 针对swipe和drag状态,整个过程中一直会调用这个函数(和ItemDecoration里面的onDrawOver()函数对应) // * 这个函数提供给我们可以在RecyclerView的上面再绘制一层东西,比如绘制一层蒙层啥的 // */ // public void onChildDrawOver(@NotNull Canvas c, @NotNull RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { // super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); // } // // /** // * 针对swipe和drag状态,当手指离开之后,view回到指定位置动画的持续时间(swipe可能是回到原位,也有可能是swipe掉) // */ // public long getAnimationDuration(@NotNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) { // return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy); // } // // /** // * 针对drag状态,当itemView滑动到RecyclerView边界的时候(比如下面边界的时候),RecyclerView会scroll, // * 同时会调用该函数去获取scroller距离(不用我们处理 直接super) // */ // public int interpolateOutOfBoundsScroll(@NotNull RecyclerView recyclerView, // int viewSize, // int viewSizeOutOfBounds, // int totalSize, // long msSinceStartScroll) { // return super.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll); // } // // /** // * 针对swipe和drag状态,当swipe或者drag对应的ViewHolder改变的时候调用 // * 我们可以通过重写这个函数获取到swipe、drag开始和结束时机,viewHolder 不为空的时候是开始,空的时候是结束 // * 即处于拖动或者滑动时调用 // */ // public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { // super.onSelectedChanged(viewHolder, actionState); // //当item开始拖动时,需要处理的逻辑 // if(actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder!=null){ // viewHolder.itemView.setBackgroundColor(getResources().getColor(R.color.colorModel));//设置item拖动时样式 // int startLocation = viewHolder.getAdapterPosition();//获取正在拖动的item的起始位置 // dragStartTag = String.valueOf(startLocation);//记录起始位置 // recyclerModel = list.get(startLocation);//将正在拖动的item对象取出暂时保存 // } // } // // /** // * 针对swipe状态,是否允许swipe(滑动)操作 // */ // public boolean isItemViewSwipeEnabled() { // return true; // } // // /** // * 针对swipe状态,swipe滑动的位置超过了百分之多少就消失 // */ // public float getSwipeThreshold(@NotNull RecyclerView.ViewHolder viewHolder) { // return Integer.MAX_VALUE;//设置成足够大的值,就可以达到滑动到item不可见也不会消失的效果 // } // // /** // * 针对swipe状态,swipe的逃逸速度,换句话说就算没达到getSwipeThreshold设置的距离,达到了这个逃逸速度item也会被swipe消失掉 // */ // public float getSwipeEscapeVelocity(float defaultValue) { // return Integer.MAX_VALUE;//设置成足够大的值,就可以使得无论逃逸速度有多快也不会消失的效果 // } // // /** // * 针对swipe状态,swipe滑动的阻尼系数,设置最大滑动速度 // */ // public float getSwipeVelocityThreshold(float defaultValue) { // return defaultValue; // } // // /** // * 针对swipe状态,swipe 到达滑动消失的距离回调函数,一般在这个函数里面处理删除item的逻辑 // * 确切的来讲是swipe item滑出屏幕动画结束的时候调用 // */ // public void onSwiped(@NotNull RecyclerView.ViewHolder viewHolder, int direction) { } // // }); // helper.attachToRecyclerView(recyclerView); } public void recycler_backClick(View view){ finish(); } }

2、Activity的布局文件,activity_recycler.xml:

3、创建一个对象模型类,RecyclerModel.java:

class RecyclerModel { int leftImage; String title; String content; RecyclerModel(int leftImage,String title,String content){ this.leftImage = leftImage; this.title = title; this.content = content; } }

4、创建RecyclerView的item布局,recyclerview_item.xml:

5、创建Adapter,CustomRecyclerViewAdapter.java:

import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class CustomRecyclerViewAdapter extends RecyclerView.Adapter{ private List list; CustomRecyclerViewAdapter(List list){ this.list = list; } @NonNull @Override public CustomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item,parent,false); return new CustomViewHolder(view); } @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { RecyclerModel model = list.get(position); holder.leftImageView.setImageResource(model.leftImage); holder.titleTextView.setText(model.title); holder.contentTextView.setText(model.content); //删除按钮点击事件处理 holder.deleteTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { list.remove(holder.getAdapterPosition()); notifyItemRemoved(holder.getAdapterPosition());//更新数据 holder.itemView.setScrollX(0);//最后要将偏移量归零,防止复用产生的问题 } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.v("position","--------"+holder.getAdapterPosition()); } }); } @Override public int getItemCount() { return list.size(); } static class CustomViewHolder extends RecyclerView.ViewHolder{ private ImageView leftImageView; private TextView titleTextView; private TextView contentTextView; private TextView deleteTextView; CustomViewHolder(@NonNull View itemView) { super(itemView); leftImageView = itemView.findViewById(R.id.recycler_item_leftImage); titleTextView = itemView.findViewById(R.id.recycler_item_title); contentTextView = itemView.findViewById(R.id.recycler_item_content); deleteTextView = itemView.findViewById(R.id.recycler_item_delete); } } }

6、创建一个继承自ItemTouchHelper.Callback的类,CustomItemTouchHelperCallback.java:

import android.graphics.Canvas; import androidx.annotation.NonNull; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import java.util.List; public class CustomItemTouchHelperCallback extends ItemTouchHelper.Callback { private final int mDefaultScrollX;//itemView可滑动的最大阈值(当滑动距离超过最大阈值,松开手指时会回弹到阈值的位置) //拖动起始记录值与拖动的item暂存对象初始值要置为空 private RecyclerModel recyclerModel = null; private String dragStartTag = null; //传入的数据源 private final List list; //CustomItemTouchHelperCallback构造方法,传入默认的最大偏移量和数据源 CustomItemTouchHelperCallback(int defaultScrollX,List list) { mDefaultScrollX = defaultScrollX; this.list = list; } /** *这个方法用于让RecyclerView拦截上下滑动和左右滑动的事件 * makeMovementFlags(dragFlags, swipeFlags);dragFlags是上下方向的滑动 swipeFlags是左右方向上的滑动 */ @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); } /** * 针对drag状态,在canDropOver()返回true时,会调用该方法 * 这里需要我们自己处理拖动后互换位置的逻辑 */ @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { if(recyclerView.getAdapter() != null){//拖动过程中不断更换位置 recyclerView.getAdapter().notifyItemMoved(viewHolder.getAdapterPosition(),target.getAdapterPosition()); } return true; } /** * 针对swipe和drag状态,当一个item view在swipe、drag状态结束的时候调用 * drag状态:当手指释放的时候会调用 * swipe状态:当item从RecyclerView中删除的时候调用,一般我们会在onSwiped()函数里面删除掉指定的item view */ @Override public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); if (recyclerView.getAdapter()!=null){ //dragStartTag和recyclerModel都不为空则是drag状态 if (dragStartTag!=null && recyclerModel!=null){ viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getResources().getColor(R.color.colorSelect));//item样式恢复原样 int endLocation = viewHolder.getAdapterPosition();//获取正在拖动的item对象的最终目标位置 int start = Integer.parseInt(dragStartTag);//获取已记录的item起始位置 list.remove(start);//先删除数组里相对应的正在拖动的item对象 list.add(endLocation,recyclerModel);//在数组对应的最终目标位置插入先前保存的item对象 if (start>endLocation){ //起始位置大于目标位置,那么recyclerView局部刷新范围则是,从目标位置开始到起始位置结束范围内刷新数据 recyclerView.getAdapter().notifyItemRangeChanged(endLocation,start-endLocation+1); }else if (start= mDefaultScrollX) { // viewHolder.itemView.scrollTo(mDefaultScrollX, 0);//让item最终偏移量为阈值即可显示删除按钮 // } else { // //往右滑的时候直接恢复初始位置(此时getScrollX()永远小于阈值,因为往右滑偏移量是负值) // viewHolder.itemView.scrollTo(0, 0); // } // } } } /** * 针对swipe和drag状态,整个过程中一直会调用这个函数,随手指移动的view就是在super里面做到的(和ItemDecoration里面的onDraw()函数对应) */ @Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { //dX的值,表示手指滑动的距离,往右滑动为正,往左为负 // getScrollX()的值,表示itemView的偏移量,往右是负,往左是正 if (isCurrentlyActive) {//手指滑动时 // 基于当前位置的偏移 viewHolder.itemView.scrollTo( (int)-dX, 0); } else {//手指松开时,因为我们设置了侧滑距离无论多大也不会删除item,所以手指松开时,itemView会回滚 //判断ItemView的偏移量是否大于给定阈值,如果大于让itemView的偏移量最终停留在阈值,显示删除按钮 if (viewHolder.itemView.getScrollX() >= mDefaultScrollX) { viewHolder.itemView.scrollTo(mDefaultScrollX, 0);//让item最终偏移量为阈值即可显示删除按钮 } else { //往右滑的时候直接恢复初始位置(此时getScrollX()永远小于阈值,因为往右滑偏移量是负值) viewHolder.itemView.scrollTo(0, 0); } } } /** * 针对swipe和drag状态,当swipe或者drag对应的ViewHolder改变的时候调用 * 我们可以通过重写这个函数获取到swipe、drag开始和结束时机,viewHolder 不为空的时候是开始,空的时候是结束 * 即处于拖动或者滑动时调用 */ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { super.onSelectedChanged(viewHolder, actionState); //当item开始拖动时,需要处理的逻辑 if(actionState == ItemTouchHelper.ACTION_STATE_DRAG && viewHolder!=null){ //先将拖动起始记录值与拖动的item暂存对象初始值置为空 dragStartTag = null; recyclerModel = null; viewHolder.itemView.setBackgroundColor(viewHolder.itemView.getResources().getColor(R.color.colorModel));//设置item拖动时样式 int startLocation = viewHolder.getAdapterPosition();//获取正在拖动的item的起始位置 dragStartTag = String.valueOf(startLocation);//记录起始位置 recyclerModel = list.get(startLocation);//将正在拖动的item对象取出暂时保存 } } /** * 针对swipe状态,swipe滑动的位置超过了百分之多少就消失 */ @Override public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) { return Integer.MAX_VALUE; } /** * 针对swipe状态,swipe的逃逸速度,换句话说就算没达到getSwipeThreshold设置的距离,达到了这个逃逸速度item也会被swipe消失掉 */ @Override public float getSwipeEscapeVelocity(float defaultValue) { return Integer.MAX_VALUE;//设置成足够大的值,就可以使得无论逃逸速度有多快也不会消失的效果 } /** * 针对swipe状态,swipe 到达滑动消失的距离回调函数,一般在这个函数里面处理删除item的逻辑 * 确切的来讲是swipe item滑出屏幕动画结束的时候调用 */ @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { } }

转自: https://www.jianshu.com/p/abedfa1d6dd1



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3