概述
RecyclerView除了有强大的列表功能外,自身还带有Item拖拽和滑动功能,对于有这方面需求的开发来讲,可以节省不少时间。
使用
1. 创建RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_drag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
2. 创建ItemView
<androidx.cardview.widget.CardView
android:layout_width="66dp"
android:layout_height="66dp"
app:cardBackgroundColor="#1dd9c4"
app:cardCornerRadius="12dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="@color/white"
android:textSize="36sp"
android:textStyle="bold" />
</androidx.cardview.widget.CardView>
3. 创建Adapter
public class DragAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List data = new ArrayList();
private Context context;
public DragAdapter(Context context) {
this.context = context;
initData();
}
private void initData() {
data.clear();
for (int i = 1; i < 10; i++) {
data.add(i);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View inflate = LayoutInflater.from(context).inflate(R.layout.item_drag, null);
return new CardViewHold(inflate);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
CardViewHold viewHold = (CardViewHold) holder;
viewHold.tvContent.setText("" + data.get(position));
}
@Override
public int getItemCount() {
return data.size();
}
class CardViewHold extends RecyclerView.ViewHolder {
TextView tvContent;
public CardViewHold(@NonNull View itemView) {
super(itemView);
tvContent = itemView.findViewById(R.id.tv_content);
}
}
}
4. 设置Adapter
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
recyclerView.setLayoutManager(gridLayoutManager);
DragAdapter dragAdapter = new DragAdapter(this);
recyclerView.setAdapter(dragAdapter);
设置为3x3的表格布局。
5. 定义拖拽事件
public interface ItemDragTouchHelper {
void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target);
void onItemSelect(RecyclerView.ViewHolder source);
void onItemClear(RecyclerView.ViewHolder source);
}
定义三种事件,Item交换,Item被选中,Item选中状态清除。若是有其他需求,可以自行添加,比如删除,左右滑动等。
6. 实现拖拽事件回调
public class ItemDragTouchHelperCallback extends ItemTouchHelper.Callback {
private ItemDragTouchHelper helper;
public ItemDragTouchHelperCallback(ItemDragTouchHelper helper) {
this.helper = helper;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, 0);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
helper.onItemMove(viewHolder, target);
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
return false;
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (!recyclerView.isComputingLayout()) {
helper.onItemClear(viewHolder);
}
}
@Override
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
helper.onItemSelect(viewHolder);
}
}
}
通过继承ItemTouchHelper.Callback可以实现具体的拖拽功能回调。这里设置的是可以在四个方向进行长按拖拽,但不可滑动。
getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
public abstract int getMovementFlags(@NonNull RecyclerView recyclerView,
@NonNull ViewHolder viewHolder);
- ItemTouchHelper.UP :往上拖
- ItemTouchHelper.DOWN:往下拖
- ItemTouchHelper.LEFT:往左拖
- ItemTouchHelper.RIGHT:往右拖
同时取这几个值就可以实现多个方向的拖拽,若是对方向有要求可以根据需要单独设置。
makeMovementFlags(int dragFlags, int swipeFlags)
public static int makeMovementFlags(int dragFlags, int swipeFlags) {
return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
| makeFlag(ACTION_STATE_SWIPE, swipeFlags)
| makeFlag(ACTION_STATE_DRAG, dragFlags);
}
- dragFlags:拖拽方向
- swipeFlags:滑动方向
onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target)
public abstract boolean onMove(@NonNull RecyclerView recyclerView,
@NonNull ViewHolder viewHolder, @NonNull ViewHolder target);
- recyclerView:有长按拖拽功能的recyclerView
- viewHolder:手指长按后被拖拽的Item
- target:拖拽目标位置的Item
当返回true时,进行拖拽从一个位置到另一个位置会调用该方法,具体变换逻辑用户自行处理。
onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
public abstract void onSwiped(@NonNull ViewHolder viewHolder, int direction);
- viewHolder:滑动Item
- direction:滑动方向,建议使用START or END代替LEFT or RIGHT
若是没有滑动功能就不做处理。
isLongPressDragEnabled
返回true可长按拖动,即本文指的拖拽功能。
isItemViewSwipeEnabled
返回false即Item不可滑动。
clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
public void clearView(@NonNull RecyclerView recyclerView, @NonNull ViewHolder viewHolder) {
ItemTouchUIUtilImpl.INSTANCE.clearView(viewHolder.itemView);
}
交互完成后调用该方法,此时可以做相关处理,关闭动画,开启其他动画,恢复正常状态等,但需要等RecycleView计算布局完成后再。
onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState)
public void onSelectedChanged(@Nullable ViewHolder viewHolder, int actionState) {
if (viewHolder != null) {
ItemTouchUIUtilImpl.INSTANCE.onSelected(viewHolder.itemView);
}
}
- viewHolder:被选中的Item
- actionState:行为状态, ACTION_STATE_IDLE, ACTION_STATE_SWIPE :滑动行为,ACTION_STATE_DRAG:拖拽行为
Item被选中后调用该方法,作用和clearView(RecyclerView, RecyclerView.ViewHolder)差不多。 需要重写的方法还有一些,可根据具体需求选择性重写。
7. Adapter进行具体的拖拽逻辑处理
public class DragAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ItemDragTouchHelper {
...
@Override
public void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
int fromPosition = source.getBindingAdapterPosition();
int toPosition = target.getBindingAdapterPosition();
if (fromPosition < data.size() && toPosition < data.size()) {
Collections.swap(data, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
onItemClear(source);
}
@Override
public void onItemSelect(RecyclerView.ViewHolder source) {
source.itemView.setScaleX(1.2f);
source.itemView.setScaleY(1.2f);
}
@Override
public void onItemClear(RecyclerView.ViewHolder source) {
source.itemView.setScaleX(1.0f);
source.itemView.setScaleY(1.0f);
}
}
通过让Adapter实现ItemDragTouchHelper接口,进行具体的逻辑逻辑处理。
- onItemMove:通过获取被拖拽的item和目标item的绑定位置,通过swap(List<?> list, int i, int j)对数据进行交换,再调用notifyItemMoved进行页面酸性和换位动画。
- onItemSelect:Item被选中后让被选中的Item变大为原来的1.2倍。
- onItemClear:换位完成后,让被选中的Item恢复到原来的样子。
8. 开启拖拽功能
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 3);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(16, 16, 16, 16);
}
});
DragAdapter dragAdapter = new DragAdapter(this);
recyclerView.setAdapter(dragAdapter);
ItemTouchHelper.Callback callback = new ItemDragTouchHelperCallback(dragAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
- addItemDecoration:设置item的装饰,这里设置上下左右的偏移量为16px,若有特殊需要用户可自定义ItemDecoration。
- ItemDragTouchHelperCallback(dragAdapter):创建之前定义好的拖拽逻辑处理回调。
- attachToRecyclerView:连接RecyclerView的拖拽事件,实现定义好的功能。
预览
|