项目需求需要类似发布朋友圈拖拽排序的功能,故此记录一下,感谢Android SDK的强大。
API链接:https://developer.android.com/reference/android/support/v7/widget/helper/ItemTouchHelper.Callback
效果视频连接:https://blog.csdn.net/CCstar1/article/details/89284445
网上有很多使用GirdView实现拖拽排序效果相对来说比较复杂,在保证效率和质量的情况下我这里选择使用RecycleView,官方提供了TiemTouchHelper来支持此效果的完成,它的出现本意是上下拖拽实现排序,左右拖拽实现删除(此处非瀑布流)。但是实现拖拽效果已经绰绰有余了。下面我们开始吧。
TiemTouchHelper:(官方解释)
此类是ItemTouchHelper与您的应用程序之间的契约。它允许您控制每个ViewHolder启用触摸行为,并在用户执行这些操作时接收回调。
要控制用户可以对每个视图执行哪些操作,您应该覆盖 getMovementFlags(RecyclerView, ViewHolder)并返回适当的方向标志集。(LEFT,RIGHT,START,END, UP,DOWN)。您可以使用 makeMovementFlags(int, int)它轻松构建它。或者,您可以使用 ItemTouchHelper.SimpleCallback。
如果用户拖动item,ItemTouchHelper将调用 onMove(recyclerView, dragged, target)。收到此回调后,您应该将项目从旧位置(dragged.getAdapterPosition())移动到target.getAdapterPosition()适配器中的新位置()并调用notifyItemMoved(int, int)。要控制可以删除视图的位置,您可以覆盖 canDropOver(RecyclerView, ViewHolder, ViewHolder)。当拖动视图与多个其他视图重叠时,Callback会选择最近的视图,拖动的视图可能会更改位置。虽然此方法适用于许多用例,但如果您有自定义LayoutManager,则可以覆盖 chooseDropTarget(ViewHolder, java.util.List, int, int)以选择自定义放置目标。
滑动视图时,ItemTouchHelper会对其进行动画处理,直到它超出范围,然后调用 onSwiped(ViewHolder, int)。此时,您应该更新适配器(例如删除item)并调用相关的Adapter#notify事件。
自定义了Recycleview实现拖拽,更换位置有数据换位置然后刷新实现并非Recycleview自己实现,代码如下:
public class DragItemRecyclerView extends RecyclerView {
private ItemTouchHelper itemTouchHelper;
private DragAdapter dragAdapter;
private float ALPHA_FULL = 1.0f;
public DragItemRecyclerView(Context context) {
super(context);
}
public DragItemRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public DragItemRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
dragAdapter = (DragAdapter) adapter;
init();
}
private void init() {
this.getItemAnimator().setChangeDuration(0);
if (itemTouchHelper == null) {
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback());
itemTouchHelper.attachToRecyclerView(this);
}
}
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
int endPosition, startPosition;
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
if (dragAdapter.list != null && !TextUtils.isEmpty(dragAdapter.list.get(viewHolder.getAdapterPosition())) && dragAdapter.list.get(viewHolder.getAdapterPosition()).equals(CreateTopicActivity.ADD_IMAGE)) {
return makeMovementFlags(0, 0);
}
if (dragAdapter.list != null && !TextUtils.isEmpty(dragAdapter.list.get(viewHolder.getAdapterPosition())) && !PhotoTools.isImageFile(dragAdapter.list.get(viewHolder.getAdapterPosition()))) {
return makeMovementFlags(0, 0);
}
if (recyclerView.getLayoutManager() instanceof GridLayoutManager || recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
//不需要滑动
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
@Override
public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) {
if (viewHolder.getItemViewType() != target.getItemViewType()) {
return false;
}
startPosition = viewHolder.getAdapterPosition();
endPosition = target.getAdapterPosition();
return dragAdapter.onItemMove(startPosition, endPosition);
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (dragAdapter != null) {
dragAdapter.notifyItemRangeChanged(Math.min(startPosition, endPosition), Math.abs(startPosition - endPosition) + 1);
}
}
@Override
public boolean canDropOver(RecyclerView recyclerView, ViewHolder current, ViewHolder target) {
return true;
}
@Override
public void onSwiped(ViewHolder viewHolder, int direction) {
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@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) {
//自定义滑动动画
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
}
public interface ItemTouchHelperAdapter {
//数据交换
boolean onItemMove(int fromPosition, int toPosition);
}
}
adapter相关代码,主要是onMore调起在adapter内方法,根据返回更换位置item的position更换数据位置然后notify,代码如下:
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
if (list.get(toPosition).equals(CreateTopicActivity.ADD_IMAGE))
return false;
if (!PhotoTools.isImageFile(list.get(toPosition)))
return false;
//交换位置
Collections.swap(list, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
上述第一个if是判断是不是添加按钮,如果是不更换,需求默认添加按钮在最后。第二个if判断是否是视频,视频需求默认第一位也不可更换位置。
大家没有看错就这点代码实现了所有功能,如果大家还有什么问题欢迎留言。