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

来源:互联网 发布:本地连接服务器数据库 编辑:程序博客网 时间:2024/05/14 09:54

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

1 0
原创粉丝点击