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