实现需求:侧边栏有两个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的时机无法把握可能存在问题。