跨RecyclerView的拖动排序

本文探讨了在特定场景下如何实现两个RecyclerView之间的物品拖动排序,通过源生方式的改进和自定义逻辑,解决了位置同步、事件分配及精确操作等问题。重点介绍了移动Item的方法和判断条件,以及如何通过添加隐藏项触发跨RecyclerView的排序。
摘要由CSDN通过智能技术生成

实现需求:侧边栏有两个RecyclerView,需要拖动一个RecyclerView里面的Item项到另一个RecyclerView里面,然后触发这个RecyclerView的拖动排序。

在这里插入图片描述

初次实现的思路:使用RecyclerView源生的拖拽排序支持,ItemTouchHelper.Callback 实现;在Item的longclick时主动调用ItemTouchHelper.startDrag(viewHolder)触发RecyclerView的拖动排序,这样就实现了本RecyclerView的拖动排序。尝试跨RecyclerView的拖动,使用Item的longclick触发

同时调用RecyclerView一个隐藏项的ItemTouchHelper.startDrag(viewHolder),存在的问题:RecyclerView里面drag里面touch使用的getX和getY,所以存在拖动view和RecyclerView的dragview无法位置统一,而且touch事件存在分配的问题,有时无法让另一个RecyclerView获取到touch事件,需要主

动调用。就挤压效果和挤压判断来说,源生的肯定是更平滑的,但是针对该特殊场景会无法适配实现。

@Override
public boolean onItemMove(int fromPosition, int toPosition) {
try {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(objects, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i–) {
Collections.swap(objects, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);

        if (refreshDataListener != null) {
            refreshDataListener.setItemDataMove();
        }
    } catch (Exception e){
        e.printStackTrace();
    }
    return true;
}

再次换方法实现:参考https://www.jianshu.com/p/e2c6a315daef 的实现,自己在touch事件中进行拖动、换RecyclerView、调换位置等判断,抽取自己需要的方法并进行对应的优化,拖动Item后如果跨RecyclerView则add一个不显示的item到对应的RecyclerView位置

然后继续执行排序的判断。需要进行精确的计算和判断,同时需要控制对应的item显示和隐藏,优化后满足该需要场景,两个RecyclerView的跨RecyclerView拖动排序。具体的应用场景需要根据自己的需求进行调整。

核心的方法
/**
* 当用户拖动的时候,计算是否需要移动Item
*
* @param touchRawX float event.getRawX()
* @param touchRawY float event.getRawY()
*/
private boolean moveIfNecessary(float touchRawX, float touchRawY) {
boolean result = true;

    //找到当前触摸点下的recyclerView
    float[] location = getInsideLocation(mSideScreenPanelLinearView, touchRawX, touchRawY);
    View view = mSideScreenPanelLinearView.findChildViewUnder(location[0], location[1]);
    RecyclerView recyclerView = findRecyclerView(view, touchRawX, touchRawY);
    int recyclerPos = getPositionByChildView(view);
    //没有找到所属位置或者目标RecyclerView,则不继续
    if (recyclerView == null) {
        return false;
    }
    if(recyclerView instanceof WithoutDropRecyclerView){
        return false;
    }

    //找到当前触摸点下的itemView
    location = getInsideLocation(recyclerView, touchRawX, touchRawY);
    float itemX = location[0];
    float itemY = location[1];
    View itemView = recyclerView.findChildViewUnder(itemX, itemY);
    int itemPos = getTargetItemPos(itemView, itemY, lastRecyclerPos, recyclerPos);

    if (isSelectedRecyclerView(recyclerView)) {
        lastRecyclerPos = recyclerPos;
        lastRecyclerView = recyclerView;
    } else if (isChangeRecyclerView(recyclerView)) {
        itemPos = calcItemPositionWhenChangeRecycler(recyclerView, itemView, itemPos, itemX, itemY);
        if (itemPos != NONE) {
            boolean isChanged = dragListener.onRecyclerChanged(recyclerView, itemPos);
            if (!isChanged) {
                return result;
            }
            //在切换recycle view并且触摸到子recycle view的item的时候才真正去改变值
            lastRecyclerPos = recyclerPos;
            lastRecyclerView = recyclerView;
            //因为切换父控件,所以需要重置为当前itemPos,不然上个的最后位置有可能超过当前的大小抛出错误
            if(!mChangePositionInit) {
                mChangePositionInit = true;
                lastItemPos = itemPos;
            }
        }
    } else {
        lastRecyclerView = recyclerView;
    }

    if (itemPos == NONE) {
        return result;
    }
    if (isItemNeedChange(itemView, lastItemPos, itemPos, itemY)) {
        boolean isChanged = dragListener.onItemChanged(recyclerView, lastItemPos, itemPos);
        if (!isChanged) {
            return result;
        }
        scrollToRightPositionWhenItemChanged(recyclerView, itemView, itemPos);
        lastItemPos = itemPos;
    }
    return result;
}

/**
* 移动Item的位置 包括数据源和界面的移动
* @param fromPosition Item之前所在位置
* @param toPosition Item新的位置
*/
public void moveDataItem(int fromPosition, int toPosition) {
try {
if(toPosition < objects.size() && fromPosition < objects.size()) {
AppInfo baseInfo = (AppInfo) objects.get(fromPosition);
if(mDragAppInfo != null && mDragAppInfo.equals(baseInfo)){
objects.remove(baseInfo);
objects.add(toPosition, baseInfo);
notifyItemMoved(fromPosition, toPosition);
} else if(objects.contains(mDragAppInfo)){
objects.remove(mDragAppInfo);
objects.add(toPosition, mDragAppInfo);
notifyDataSetChanged();
} else {
BaseInfo removeBaseInfo = objects.remove(fromPosition);
objects.add(toPosition, removeBaseInfo);
notifyItemMoved(fromPosition, toPosition);
}
}
} catch (Exception e){
e.printStackTrace();
}
}

总结:特殊场景时特殊需求,源生的实现方式会有一些限制,所以需要参考源生的实现方式进行优化适配。结合上面两个实现思路,分析出的另一个实现思路,一个RecyclerView拖动时不直接触发另一个的drag拖动,再跨RecyclerView判断时再进行添加一个不显示的item,同时触发该item的drag拖动,然后只需要使用源生的drag排序流程,可能存在的问题,touch事件的传递可能存在问题,添加到触发drag的时机无法把握可能存在问题。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值