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

前言: 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实现的滑动条目进行处理操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值