Android開發 RecyclerView實現拖動與滑動ItemTouchHelper


前言

  RecyclerView依靠ItemTouchHelper,實現item的拖動與滑動功能。

了解重寫方法

  ItemTouchHelper提供了大量的重寫方法,讓你自己實現需要的組合。需要一一了解。

是否開啟長按拖動

 

    @Override
    public boolean isLongPressDragEnabled() {
        return false;
    }

 

是否開啟ItemView的滑動

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

觸發拖動的百分比

    // 針對drag狀態,當滑動超過多少就可以觸發onMove()方法(這里指onMove()方法的調用,並不是隨手指移動的View)
    @Override
    public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return super.getMoveThreshold(viewHolder);
    }

觸發滑動的百分比

    // 側滑事件的滑動距離觸發值
    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return super.getSwipeThreshold(viewHolder);
    }

觸發滑動的滑動速度

    // 側滑事件的速度觸發值
    @Override
    public float getSwipeEscapeVelocity(float defaultValue) {
        return super.getSwipeEscapeVelocity(defaultValue);
    }

設置支持的拖動或者滑動的方向

這是一個重要的方法,設置此方法可以實現只能上拖還是下拖 或者 都可以。 也可以設置又支持拖動又支持滑動

    @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);
    }

正在拖動的回調方法

這是重要的方法

    /**
     * onMove 
     */
    @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) {
    }

正在ItemView的動畫繪制方法

    /**
     * 針對swipe和drag狀態,整個過程中一直會調用這個函數,隨手指移動的view就是在super里面做到的(和ItemDecoration里面的onDraw()函數對應)
     */
    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
    }

動畫的持續時間

    /**
     * 針對swipe和drag狀態,當手指離開之后,view回到指定位置動畫的持續時間(swipe可能是回到原位,也有可能是swipe掉)
     */
    @Override
    public long getAnimationDuration(@NonNull RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
        return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
    }

執行完拖動或者滑動后,對應清理View的方法

    /**
     * 針對swipe和drag狀態,當一個item view在swipe、drag狀態結束的時候調用
     * drag狀態:當手指釋放的時候會調用
     * swipe狀態:當item從RecyclerView中刪除的時候調用,一般我們會在onSwiped()函數里面刪除掉指定的item view
     */
    @Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
    }

一些不常用的方法

    /**
     * 針對drag狀態,當前target對應的item是否允許移動
     * 我們一般用drag來做一些換位置的操作,就是當前對應的target對應的Item可以移動
     */
    @Override
    public boolean canDropOver(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder current, @NonNull RecyclerView.ViewHolder target) {
        return super.canDropOver(recyclerView, current, target);
    }

    /**
     * 針對drag狀態,當itemView滑動到RecyclerView邊界的時候(比如下面邊界的時候),RecyclerView會scroll,
     * 同時會調用該函數去獲取scroller距離(不用我們處理 直接super)
     */
    @Override
    public int interpolateOutOfBoundsScroll(@NonNull RecyclerView recyclerView, int viewSize, int viewSizeOutOfBounds, int totalSize, long msSinceStartScroll) {
        return super.interpolateOutOfBoundsScroll(recyclerView, viewSize, viewSizeOutOfBounds, totalSize, msSinceStartScroll);
    }

    /**
     * 針對swipe和drag狀態,當swipe或者drag對應的ViewHolder改變的時候調用
     * 我們可以通過重寫這個函數獲取到swipe、drag開始和結束時機,viewHolder 不為空的時候是開始,空的時候是結束
     */
    @Override
    public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);

    }

使用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的寬度

滾動整個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

// 側滑事件的滑動距離觸發值
@Override
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return super.getSwipeThreshold(viewHolder);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM