跨多个RecyclerView拖动排序

先介绍一下本文出现的背景,项目中有这样一个需求,拖动一个列表的某一项排序或者拖动到另外一个列表中。

其效果如下图:

这里写图片描述

拖动排序在RecyclerView有现成的解决方案,配合ItemTouchHelper即可轻松实现。但是要将一个列表中的Item拖动到另一个列表中去,就不那么容易了。

先来看看这种跨界面实现由那些难点,我们先分析,再一个一个的解决。

  • 拖动视图的保存,ItemTouchHelper的实现思路是获取到长按的View后,对这个View做Translate位移,但是这样做的局限在于这个View就只能在它所属的RecyclerView中移动,超出的部分就被隐藏了。
  • 当拖动Item到边缘后,两个界面怎么切换,是使用系统的ViewPager还是自定义ViewGroup。
  • 在一个RecyclerView中拖动和跨多个拖动用到的策略是否一样。
  • 由于RecyclerView已经接收了触摸事件,那么怎么获取触摸信息,比如判断滑到边缘了,以及Item是否长按。

现在我们来一个一个的解决上面的问题:
1. 对于第一个问题,解决方法是当判断Item被长按后,将当前Item记录为一张图片,然后设置给一个隐藏的ImageView,对这个ImageView做Translate平移。这里需要额外费心的是这个ImageView与长按Item视图的重合以及显隐判断。
2. 如果使用ViewPager,由于要跨越两个page,那么触摸事件,保存的图片等都要交给其父控制器,还不如自定义一个ViewGroup。
3. 由于系统提供了在一个RecyclerView中拖动的Helper类,我们无需去解决拖动的起始位置这些,那何乐不为呢?所以,跨界面拖动用自己的实现。
4. 触摸point信息的获取:dispatchTouchEvent方法是最先到的,在我们自定义的ViewPager中重写该方法。在adapter中对itemview添加手势监听,获取到被长按的View。

下面来看代码实现:

(1) 自定义的父视图:

public class NewLinearLayout extends LinearLayout {

    /*将触摸点坐标传递出去*/
    private IGetX iGetX;
    /*弹性滑动*/
    private Scroller mScroller;

    public NewLinearLayout(Context context) {
        super(context);
        init(context);
    }

    public NewLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public NewLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    /*initial*/
    public void init(Context context){
        mScroller = new Scroller(context);
    }

    @Override
    public void computeScroll(){
        super.computeScroll();
        /*判断是否完成滑动*/
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX() , 0);
            /*循环调用该方法*/
            postInvalidate();
        }
    }

    /*开启弹性滑动*/
    public void beginScroll(int dx ){
        mScroller.startScroll(mScroller.getCurrX() , 0 , dx , 0 , 1000);
        /*调用computeScroll()方法*/
        invalidate();
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event){
        float dx = event.getX();
        float dy = event.getY();
        if(event.getAction() == MotionEvent.ACTION_MOVE)
            iGetX.point(dx , dy);
        return super.dispatchTouchEvent(event);
    }

    public void set(IGetX iGetX){
        this.iGetX = iGetX;
    }

    public interface IGetX{
        void point(float dx , float dy);
    }
}

(2) View转bitmap

    public Bitmap view2bitmap(View view) {
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        view.draw(new Canvas(bitmap));
        return bitmap;
    }

(3) Adapter的实现

    private class Recycler1Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        private ArrayList<String> mData;

        public void setData(ArrayList<String> mData) {
            this.mData = mData;
        }

        public void addData(ArrayList<String> mData) {
            this.mData.addAll(mData);
            notifyDataSetChanged();
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View itemView = LayoutInflater.from(SpanDragActivity.this).inflate(R.layout.item_launch_title, null);
            final Recycler1ViewHolder viewHolder = new Recycler1ViewHolder(itemView);
            viewHolder.itemView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    mDetector.onTouchEvent(event);
                    if(event.getAction() == MotionEvent.ACTION_DOWN){
                        bitmap = null;
                        TempItemView = viewHolder.itemView;
                        startX = TempItemView.getLeft();
                        startY = TempItemView.getTop();
                    }
                    /*恢复计点*/
                    if(event.getAction() == MotionEvent.ACTION_UP){
                        isFirstPoint = true;
                    }

                    return false;
                }
            });
            return viewHolder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            ((Recycler1ViewHolder) holder).mTv.setText(mData.get(position));
        }

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

        /*create viewholder*/
        class Recycler1ViewHolder extends RecyclerView.ViewHolder {

            private TextView mTv;

            public Recycler1ViewHolder(View itemView) {
                super(itemView);
                mTv = (TextView) itemView.findViewById(R.id.item_launch_title_tv_title);
            }
        }
    }

(4) 滑动到边缘后触发的事件

        /*设置接口获取手指位置*/
        activitySpanLl.set(new NewLinearLayout.IGetX() {
            @Override
            public void point(float dx , float dy) {
                Log.v("out", "手指位置:" + dx);
                if(isFirstPoint){
                    firstX = dx;
                    firstY = dy;
                    isFirstPoint = false;
                }
                if(bitmap != null) {
                    activitySpanImg.setTranslationX(dx-firstX+startX);
                    activitySpanImg.setTranslationY(dy-firstY+startY);
                }
                if(dx >=800){
                }
                if (isFirstScroll)
                    if (dx >= 1000) {
                        activitySpanLl.beginScroll(1080);
                        isFirstScroll = false;
                        /*向右边滑动了,所以现在可以向左滑动了*/
                        isFirstLeftScroll = true;
                        int a = activitySpanLl.getScrollX();
                        float b = activitySpanLl.getX();
                    }
                if(isFirstLeftScroll)
                    if(dx <= 80){
                        activitySpanLl.beginScroll(-1080);
                        isFirstLeftScroll = false;
                        /*向左边滑动了,所以现在可以向右滑动了*/
                        isFirstScroll = true;
                        int a = activitySpanLl.getScrollX();
                        float b = activitySpanLl.getX();
                    }
            }
        });
基本思路和重要的代码都在上面了,在实际项目中应用还需要对坐标,滑动等作出优化。

有更多问题留言交流。

注意:
因为电脑更换,本demo源码已经找不到了,但是鉴于很多人需要,所以这周我重新写一个demo,需要的先加这个QQ群(之前由于一些原因博客停止更新,接下来会继续更新,主要是音视频相关的,也可以入群交流)

                            703660703

欢迎入群

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 24
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值