RecyclerView实现滑动和拖拽功能(带小例子)

    xiaoxiao2021-03-25  146

    前言: RecyclerView相对于ListView实现拖拽和滑动的效果要容易很多,今天写一个小程序,在上一篇文章 RecyclerView+CardView使用总结(带小例子) 基础上实现RecyclerView条目的上下拖拽和滑动删除,效果图如下:

    第一步:设置拖动和滑动的回掉,让recyclerView和回调处理关联起来 主要代码如下:

    mRecyclerView = (RecyclerView) mView.findViewById(R.id.hot_fragment_rcv); /*1,设置管理器*/ mRecyclerView.setLayoutManager(new LinearLayoutManager(this.getContext())); /*2,设置适配器*/ initListData(); mAdapter = new HotFgListStrAdapter(mDatas); mRecyclerView.setAdapter(mAdapter); /**设置拖动和滑动的回调*/ ItemTouchHelper.Callback callback = new RecycleItemTouchHelper(mAdapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback); itemTouchHelper.attachToRecyclerView(mRecyclerView); /*3,添加item的添加和移除动画, 这里我们使用默认的*/ mRecyclerView.setItemAnimator(new DefaultItemAnimator()); /*4,添加分割线,自定义分割线*/ mRecyclerView.addItemDecoration(new HotFgItemDecoration());

    第二步,实现拖动和滑动的方式,怎么拖动和滑动,向哪个方向拖动和滑动 ItemTouchHelper.Callback是谷歌提供的强大的工具类,处理RecyclerView拖动和滑动的实现,并且可以实现我们自己定义的动画和定制的效果。RecycleItemTouchHelper是我们继承ItemTouchHelper.Callback实现的。对于实现那几个函数,函数的作用,我在代码中做了注释,代码如下: RecycleItemTouchHelper.java

    public class RecycleItemTouchHelper extends ItemTouchHelper.Callback { private final ItemTouchHelperCallback helperCallback; public RecycleItemTouchHelper(ItemTouchHelperCallback helperCallback) { this.helperCallback = helperCallback; } /** * 该方法返回一个整数,用来指定拖拽和滑动在哪个方向是可以的。 * @param recyclerView * @param viewHolder * @return */ @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { /** * makeMovementFlags(int dargFlags, int swipeFlags) * 该方法第一个参数指定拖动,第二个参数自定滑动。参数选择有六个如下: * ItemTouchHelper.UP 滑动拖拽方向向上 * ItemTouchHelper.DOWN 向下 * ItemTouchHelper.LEFT 向左 * ItemTouchHelper.RIGHT 向右 * ItemTouchHelper.START 依赖布局方向的水平开始方法(右向左) * ItemTouchHelper.END 依赖布局方向的水平结束方向(左向右) */ //本次设置结果为 支持上下拖拽,向右滑动 int flag = makeMovementFlags(ItemTouchHelper.UP|ItemTouchHelper.DOWN, ItemTouchHelper.END); return flag; } /** * 该方法是拖拽的回掉 * @param recyclerView * @param viewHolder 表示拖动的item * @param target 表示拖动的目标位置的item * @return 如果item切换的位置返回true, 否则返回false */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { helperCallback.onMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); return true; } /** * 该方法为item滑动的回掉 * @param viewHolder 表示滑动的item * @param direction 表示滑动的方向 */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { helperCallback.onItemDelete(viewHolder.getAdapterPosition()); } //*********上面的三个方法是必须实现的*********下面三个方法可选择实现 /** * item是否支持长按拖动,true支持,false不支持 * @return */ @Override public boolean isLongPressDragEnabled() { return super.isLongPressDragEnabled(); } /** * item是否支持滑动,true支持,false不支持 * @return */ @Override public boolean isItemViewSwipeEnabled() { return super.isItemViewSwipeEnabled(); } /** * 移动过程中绘制item * @param c * @param recyclerView * @param viewHolder * @param dX 表示x轴移动距离 * @param dY 表示y轴移动距离 * @param actionState 当前item的状态 * @param isCurrentlyActive 如果当前用户在操作则为true,否则为false */ @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }

    方法说明,我在注释中已经解释,这里就不再解释了,注意: 如果实现拖动或者滑动必须将上面的是否支持拖动isLongPressDragEnabled()和是否支持滑动isItemViewSwipeEnabled()的方法返回true,否则onMove()或者onSwiped()方法不会执行。默认情况下isLongPressDragEnabled()和isItemViewSwipeEnabled()返回true; ItemTouchHelperCallback是个接口,包含处理拖动和滑动后的方法,代码如下:ItemTouchHelperCallback.java

    public interface ItemTouchHelperCallback { /** * 移动item * @param fromPosition 起始位置 * @param toPosition 结束位置 */ void onMove(int fromPosition, int toPosition); /** * 删除item * @param position */ void onItemDelete(int position); }

    第三步:对拖动和滑动后的事件进行响应处理 对RecyclerView进行拖动后,条目的位置变化了,滑动后,条目移除了,这些变化最终的展示效果,必须通知Adapter来实现,所以Adapter实现接口ItemTouchHelperCallback,具体代码如下:HotFgListStrAdapter.java

    public class HotFgListStrAdapter extends RecyclerView.Adapter<HotFgListStrAdapter.TextViewHolder> implements ItemTouchHelperCallback { private List<String> mDatas; private LayoutInflater mInflater; public HotFgListStrAdapter(List<String> mDatas) { this.mDatas = mDatas; } @Override public TextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { mInflater = LayoutInflater.from(parent.getContext()); return new TextViewHolder(mInflater.inflate(R.layout.item_hot_fg_list2, parent, false)); } @Override public void onBindViewHolder(TextViewHolder holder, final int position) { holder.mTvTitle.setText(mDatas.get(position)); holder.mTvTitle2.setText(mDatas.get(position)); } @Override public int getItemCount() { return mDatas.size(); } @Override public void onMove(int fromPosition, int toPosition) { Collections.swap(mDatas, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition); } @Override public void onItemDelete(int position) { mDatas.remove(position); notifyItemRemoved(position); } /** * 文字item的holder */ class TextViewHolder extends RecyclerView.ViewHolder{ private TextView mTvTitle, mTvTitle2; public TextViewHolder(View itemView) { super(itemView); mTvTitle = (TextView) itemView.findViewById(R.id.hot_fg_item_tv); mTvTitle2 = (TextView) itemView.findViewById(R.id.hot_fg_item_tv2); } } }

    notifyItemMoved(…)、notifyItemRemoved(…)、notifyItemChanged(…)等等针对一条或者连续多条数据进行更新,这个很方便,不用因为一条数据改变,而调用notifyDataSetChanged()来通知多所有数据刷新。建议使用这个方法。代码到这里其实已经实现文章开头的效果了,但是滑动删除一条条目的体验不好,如果我们滑动后显示一个删除按钮,体验会更好一些。

    第四步:功能升级,优化用户体验 现在我们在滑动过程中显示一个删除图标,删除过程中再增加一个动画。这个效果不是很麻烦,前面我们写过一个方法onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) 该方法是移动过程中绘制item的回调,当actionState == ItemTouchHelper.ACTION_STATE_SWIPE时,即为滑动时候绘制背景和删除图片,具体代码如下:

    /** * 移动过程中绘制item * @param c * @param recyclerView * @param viewHolder * @param dX 表示x轴移动距离 * @param dY 表示y轴移动距离 * @param actionState 当前item的状态 * @param isCurrentlyActive 如果当前用户在操作则为true,否则为false */ @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){ //dX大于0时向右滑动,小于0向左滑动 View itemView=viewHolder.itemView;//获取滑动的view Resources resources= recyclerView.getResources(); Bitmap bitmap= BitmapFactory.decodeResource(resources, R.drawable.address_delete);//获取删除指示的背景图片 int padding =40;//图片绘制的padding int maxDrawWidth=2*padding+bitmap.getWidth();//最大的绘制宽度 Paint paint=new Paint(); paint.setColor(resources.getColor(R.color.themeColor)); int x=Math.round(Math.abs(dX)); int drawWidth=Math.min(x,maxDrawWidth);//实际的绘制宽度,取实时滑动距离x和最大绘制距离maxDrawWidth最小值 int itemTop=itemView.getBottom()-itemView.getHeight();//绘制的top位置 //向右滑动 if(dX>0){ //根据滑动实时绘制一个背景 c.drawRect(itemView.getLeft(),itemTop,drawWidth,itemView.getBottom(),paint); //在背景上面绘制图片 if (x>padding){//滑动距离大于padding时开始绘制图片 //指定图片绘制的位置 Rect rect=new Rect();//画图的位置 rect.left=itemView.getLeft()+padding; rect.top=itemTop+(itemView.getBottom()-itemTop-bitmap.getHeight())/2;//图片居中 int maxRight=rect.left+bitmap.getWidth(); rect.right=Math.min(x,maxRight); rect.bottom=rect.top+bitmap.getHeight(); //指定图片的绘制区域 Rect rect1=null; if (x<maxRight){ rect1=new Rect();//不能再外面初始化,否则dx大于画图区域时,删除图片不显示 rect1.left=0; rect1.top = 0; rect1.bottom=bitmap.getHeight(); rect1.right=x-padding; } c.drawBitmap(bitmap,rect1,rect,paint); } //滑动透明度动画 float alpha = 1.0f - Math.abs(dX) / (float) itemView.getWidth(); itemView.setAlpha(alpha); //绘制时需调用平移动画,否则滑动看不到反馈 itemView.setTranslationX(dX); }else { //如果在getMovementFlags指定了向左滑动(ItemTouchHelper。START)时则绘制工作可参考向右的滑动绘制,也可直接使用下面语句交友系统自己处理 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }else { //拖动时有系统自己完成 super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }

    这一步做完后,运行效果如下: 文章到这里就结束了,文章有不对的地方,欢迎指正^_^。这里推荐github上的一个开源项目,继承ListView实现的滑动条目进行处理操作。

    转载请注明原文地址: https://ju.6miu.com/read-22322.html

    最新回复(0)