Android开发 RecyclerView实现拖动与滑动ItemTouchHelper
前言
RecyclerView依靠ItemTouchHelper,实现item的拖动与滑动功能。
使用ItemTouchHelper实现上下拖动的例子
首先我们需要继承重写 ItemTouchHelper.Callback
public class QuickReplyItemTouchCallback extends ItemTouchHelper.Callback { private QuickReplyAdapter mAdapter; private boolean mIsLongPressDragEnabled = true; public QuickReplyItemTouchCallback(QuickReplyAdapter adapter) { //传入适配器 mAdapter = adapter; } public void setLongPressDragEnabled(boolean isLongPressDragEnabled) { mIsLongPressDragEnabled = isLongPressDragEnabled; } @Override public boolean isItemViewSwipeEnabled() { //是否启用左右滑动 return false; } @Override public boolean isLongPressDragEnabled() { //返回设置是否长按拖动Item上下移动 return mIsLongPressDragEnabled; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { //在这个回调方法里我们返回我们需要的使用的动作功能 int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; //拖动 这里设置的UP 与 DOWN 表示允许上下拖动 int swipeFlags = ItemTouchHelper.ACTION_STATE_IDLE; //滑动 这里设置的ACTION_STATE_IDLE 表示我们将滑动动作设置为空闲 return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { //用于上下移动Item的回调方法,在这个方法里我们要主动将Adapter里的数据互相替换位置 mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); //返回 true表示我们已经将Adapter里的数据互相替换位置 return true; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { //因为我们不需要处理滑动,所以此处不写逻辑 } }
Adapter更换位置的实现
/** * 提供给QuickReplyItemTouchCallback类使用的移动Item位置的方法 * @param fromPosition * @param toPosition */ public void onItemMove(int fromPosition, int toPosition) { Collections.swap(mList, fromPosition, toPosition);//更换我们数据List的位置 notifyItemMoved(fromPosition, toPosition); //更换Adapter Item的视图位置 if (mOnChangeDataPositionListener != null){ mOnChangeDataPositionListener.onChange(mList); } }
在Activity里给RecyclerView添加ItemTouchHelper.Callback
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false); mQuickReplyRecyclerView.setLayoutManager(linearLayoutManager); mQuickReplyAdapter = new QuickReplyAdapter(); mQuickReplyRecyclerView.setAdapter(mQuickReplyAdapter); mCallback = new QuickReplyItemTouchCallback(mQuickReplyAdapter); ItemTouchHelper itemTouchHelper = new ItemTouchHelper(mCallback); itemTouchHelper.attachToRecyclerView(mQuickReplyRecyclerView);
使用ItemTouchHelper实现左右滑动的例子
下面的这个例子是改变需要出现的View的宽度
public class SwitchListItemTouchCallback extends ItemTouchHelper.Callback { private int mWidth = 0; @Override public boolean isLongPressDragEnabled() { return false; } @Override public boolean isItemViewSwipeEnabled() { return true; } @Override public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.ACTION_STATE_IDLE; int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; } @Override public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { } @Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { //判断活动状态为左右滑动 Context context = recyclerView.getContext(); if (mWidth == 0) { mWidth = UnitConversionUtil.dip2px(context, 80); //Button显示后最大的宽度。这里用了一个dp转px工具类 } if (dX == 0){ //有时候点击也会被触发成swipe,这里判断不发生偏移量就跳过 return; } boolean isLeft = dX < 0; SwitchListAdapter.ViewHolder itemViewHolder = (SwitchListAdapter.ViewHolder) viewHolder; ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) itemViewHolder.mDeleteBtn.getLayoutParams(); if (isLeft) { layoutParams.width = Math.min((int) Math.abs(dX), mWidth); } else { layoutParams.width = Math.max((int) (mWidth - dX), 0); } itemViewHolder.mDeleteBtn.setLayoutParams(layoutParams); } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } } @Override public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { // super.clearView(recyclerView, viewHolder); } }
滚动整个View来实现,这个效果的关键点是那个需要隐藏或者显示的View 需要在父类布局的外面
布局例子:
关键点 app:layout_constraintLeft_toRightOf="parent"
<ImageButton android:id="@+id/delete_btn" android:layout_width="80dp" android:layout_height="0dp" android:background="@color/color_text_yellow" android:src="@drawable/ic_delete_2" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
代码
@Override public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { Context context = recyclerView.getContext(); if (mWidth == 0) { mWidth = UnitConversionUtil.dip2px(context, 80); } if (dX == 0){ //有时候点击也会被触发成swipe,这里判断不发生偏移量就跳过 return; } boolean isLeft = dX < 0; SwitchListAdapter.ViewHolder itemViewHolder = (SwitchListAdapter.ViewHolder) viewHolder; if (isLeft) { itemViewHolder.rootView.setScrollX(Math.min((int) Math.abs(dX), mWidth)); } else { itemViewHolder.rootView.setScrollX(Math.max((int) (mWidth - dX), 0)); } } else { super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); } }
End