Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> RecyclerView實現條目Item拖拽排序與滑動刪除

RecyclerView實現條目Item拖拽排序與滑動刪除

編輯:關於Android編程

效果演示

效果演示

需求和技術分析

RecyclerView Item拖拽排序::長按RecyclerView的Item或者觸摸Item的某個按鈕。 RecyclerView Item滑動刪除:RecyclerView Item滑動刪除:RecyclerView的Item滑動刪除。

實現方案與技術

利用ItemTouchHelper綁定RecyclerViewItemTouchHelper.Callback來實現UI更新,並且實現動態控制是否開啟拖拽功能和滑動刪除功能。

實現步驟

繼承抽象類ItemTouchHelper,並在構造方法傳入實現的ItemTouchHelper.Callback。 recyclerView綁定ItemTouchHelper:itemTouchHelper.attachToRecyclerView(recyclerView)。 自定義ItemTouchHelper.Callback的實現接口OnItemTouchCallbackListener,由外部更新RecyclerView的Item。

幾個主要的布局

activity_main.xml




    

這個沒啥好說的了吧,就是一個RecyclerView啦。

接下來是RecyclerView的Item的布局item.xml:




    

    

    

    

這個也不用解釋了,到時候下載看源碼,就是普通item,展示數據而已。

實現自己的DefaultItemTouchHelper:繼承ItemTouchHelper

public class DefaultItemTouchHelper extends ItemTouchHelper {
    public DefaultItemTouchHelper(ItemTouchHelp.Callback callback) {
        super(callback);
    }
}

好嘛,這個太簡單了,基本上一行代碼都不用寫。但是這裡需要一個ItemTouchHelp.Callback啊,所以我們還是要實現一個ItemTouchHelp.Callback,客觀且看下文分解。

實現自己的ItemTouchHelper.Callback:繼承ItemTouchHelper.Callback

這裡是全文最重要的部分啦,要認真點看噢,先上代碼,後解釋,其他看注釋和視頻。

public class DefaultItemTouchHelpCallback extends ItemTouchHelper.Callback {

    /**
     * Item操作的回調
     */
    private OnItemTouchCallbackListener onItemTouchCallbackListener;

    /**
     * 是否可以拖拽
     */
    private boolean isCanDrag = false;
    /**
     * 是否可以被滑動
     */
    private boolean isCanSwipe = false;

    public DefaultItemTouchHelpCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
        this.onItemTouchCallbackListener = onItemTouchCallbackListener;
    }

    /**
     * 設置Item操作的回調,去更新UI和數據源
     *
     * @param onItemTouchCallbackListener
     */
    public void setOnItemTouchCallbackListener(OnItemTouchCallbackListener onItemTouchCallbackListener) {
        this.onItemTouchCallbackListener = onItemTouchCallbackListener;
    }

    /**
     * 設置是否可以被拖拽
     *
     * @param canDrag 是true,否false
     */
    public void setDragEnable(boolean canDrag) {
        isCanDrag = canDrag;
    }

    /**
     * 設置是否可以被滑動
     *
     * @param canSwipe 是true,否false
     */
    public void setSwipeEnable(boolean canSwipe) {
        isCanSwipe = canSwipe;
    }

    /**
     * 當Item被長按的時候是否可以被拖拽
     *
     * @return
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return isCanDrag;
    }

    /**
     * Item是否可以被滑動(H:左右滑動,V:上下滑動)
     *
     * @return
     */
    @Override
    public boolean isItemViewSwipeEnabled() {
        return isCanSwipe;
    }

    /**
     * 當用戶拖拽或者滑動Item的時候需要我們告訴系統滑動或者拖拽的方向
     *
     * @param recyclerView
     * @param viewHolder
     * @return
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager
            // flag如果值是0,相當於這個功能被關閉
            int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            int swipeFlag = 0;
            // create make
            return makeMovementFlags(dragFlag, swipeFlag);
        } else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
            int orientation = linearLayoutManager.getOrientation();

            int dragFlag = 0;
            int swipeFlag = 0;

            // 為了方便理解,相當於分為橫著的ListView和豎著的ListView
            if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是橫向的布局
                swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            } else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是豎向的布局,相當於ListView
                dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            }
            return makeMovementFlags(dragFlag, swipeFlag);
        }
        return 0;
    }

    /**
     * 當Item被拖拽的時候被回調
     *
     * @param recyclerView     recyclerView
     * @param srcViewHolder    拖拽的ViewHolder
     * @param targetViewHolder 目的地的viewHolder
     * @return
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder srcViewHolder, ViewHolder targetViewHolder) {
        if (onItemTouchCallbackListener != null) {
            return onItemTouchCallbackListener.onMove(srcViewHolder.getAdapterPosition(), targetViewHolder.getAdapterPosition());
        }
        return false;
    }

    @Override
    public void onSwiped(ViewHolder viewHolder, int direction) {
        if (onItemTouchCallbackListener != null) {
            onItemTouchCallbackListener.onSwiped(viewHolder.getAdapterPosition());
        }
    }

    public interface OnItemTouchCallbackListener {
        /**
         * 當某個Item被滑動刪除的時候
         *
         * @param adapterPosition item的position
         */
        void onSwiped(int adapterPosition);

        /**
         * 當兩個Item位置互換的時候被回調
         *
         * @param srcPosition    拖拽的item的position
         * @param targetPosition 目的地的Item的position
         * @return 開發者處理了操作應該返回true,開發者沒有處理就返回false
         */
        boolean onMove(int srcPosition, int targetPosition);
    }
}

好,其實上面最重要的就是五個方法:

/**
 * 是否可以長按拖拽排序。
 */
@Override
public boolean isLongPressDragEnabled() {}
/**
 * Item是否可以被滑動(H:左右滑動,V:上下滑動)
 */
@Override
public boolean isItemViewSwipeEnabled() {}
/**
 * 當用戶拖拽或者滑動Item的時候需要我們告訴系統滑動或者拖拽的方向
 */
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {}
/**
 * 當Item被拖拽的時候被回調
 */
@Override
public boolean onMove(RecyclerView r, ViewHolder rholer, ViewHolder tholder) {}

/**
 * 當View被滑動刪除的時候
 */
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {}

isItemViewSwipeEnabled()返回值是否可以拖拽排序,true可以,false不可以,isItemViewSwipeEnabled()是否可以滑動刪除,true可以,false不可以;這兩個方法都是配置是否可以操作的。我們上面的代碼中返回了一個成員變量值,並且這個值通過外部可以修改,所以提供了外部控制的方法。

onMove()當Item被拖拽排序移動到另一個Item的位置的時候被回調,onSwiped()當Item被滑動刪除到不見;這兩個方法是當用戶操作了,來回調我們,我們就該去更新UI了。這裡我們提供了一個Listener去通知外部,並且返回出去了必要的值,來降低代碼耦合度。

getMovementFlags()說明一:是當用戶拖拽或者滑動Item的時候需要我們告訴系統滑動或者拖拽的方向,那我們又知道支持拖拽和滑動刪除的無非就是LinearLayoutManagerGridLayoutManager了,相當於我們老早的時候用的ListViewGridView了。所以我們根據布局管理器的不同做了響應的區分。

getMovementFlags()說明二:其他都好理解,就是這裡的return makeMovementFlags(dragFlag, swipeFlag);這句話是最終的返回值,也就是它決定了我們的拖拽或者滑動的方法。第一個參數是拖拽flag,第二個是滑動的flag。

重新定義DefaultItemTouchHelper

我們記得上面定義了一個DefaultItemTouchHelper,它的構造中需要傳一個ItemTouchHelper.Callback,既然我們實現禮了,我們再把DefaultItemTouchHelper做個封裝,使使用者更傻瓜式的調用。

public class DefaultItemTouchHelper extends YolandaItemTouchHelper {

    private DefaultItemTouchHelpCallback itemTouchHelpCallback;

    public DefaultItemTouchHelper(DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener) {
        super(new DefaultItemTouchHelpCallback(onItemTouchCallbackListener));
        itemTouchHelpCallback = (DefaultItemTouchHelpCallback) getCallback();
    }

    /**
     * 設置是否可以被拖拽
     *
     * @param canDrag 是true,否false
     */
    public void setDragEnable(boolean canDrag) {
        itemTouchHelpCallback.setDragEnable(canDrag);
    }

    /**
     * 設置是否可以被滑動
     *
     * @param canSwipe 是true,否false
     */
    public void setSwipeEnable(boolean canSwipe) {
        itemTouchHelpCallback.setSwipeEnable(canSwipe);
    }
}

現在我們看到已經不需要傳ItemTouchHelper.CallbackItemTouchHelper了,只需要傳我們在DefaultItemTouchHelpCallback中定義好的OnItemTouchCallbackListener就好了,而且提供了設置是否可以滑動和是否可以拖拽的方法,而OnItemTouchCallbackListener只是通知外部滑動了、刪除了,你去更新UI吧。

這裡可以有的同學會有疑問,上面原來不是繼承ItemTouchHelper嗎?這裡咋就變成了YolandaItemTouchHelper了呢?因為我們看到這裡多了一句itemTouchHelpCallback = getCallback();,這個getCallback();這個方法是沒有的,是我們在YolandaItemTouchHelper中自定義的,因為我們想在DefaultItemTouchHelper中提供外部設置是否可以拖拽和滑動刪除的方法,就得拿到這個Callback,所以我看了下源碼,我們在ItemTouchHelper構造中把Callback穿進去,它保存的時候一個package級別的成員變量,所以我在android.support.v7.widget.helper包下新建了一個YolandaItemTouchHelper類:

public class YolandaItemTouchHelper extends ItemTouchHelper {
    public YolandaItemTouchHelper(Callback callback) {
        super(callback);
    }

    public Callback getCallback() {
        return mCallback;
    }
}

如何投入使用

好扯淡也扯完了,封裝也封裝完了,那麼接下來就來在Activity中使用下咯:

recyclerView綁定ItemTouchHelper

沒啥好說的用itemTouchHelper.attachToRecyclerView(recyclerView)綁定recyclerViewItemTouchHelper,並且只是允許拖拽和滑動刪除Item:

DefaultItemTouchHelper itemTouchHelper = new DefaultItemTouchHelper(onItemTouchCallbackListener);
itemTouchHelper.attachToRecyclerView(recyclerView);
itemTouchHelper.setDragEnable(true);
itemTouchHelper.setSwipeEnable(true);

看到上面還缺少一個onItemTouchCallbackListener吧,這個也比較重要。

使用Callback自定義的OnItemTouchCallbackListener刷新UI

我們在自定義Callback的時候不是在onMove()onSwiped()方法中回調OnItemTouchCallbackListener去更新UI嗎?這裡就是OnItemTouchCallbackListener如何更新UI的操作了,完成這個操作,那麼我們的目的就達到了:

private DefaultItemTouchHelpCallback.OnItemTouchCallbackListener onItemTouchCallbackListener = new DefaultItemTouchHelpCallback.OnItemTouchCallbackListener() {
    @Override
    public void onSwiped(int adapterPosition) {
        // 滑動刪除的時候,從數據源移除,並刷新這個Item。
        if (userInfoList != null) {
            userInfoList.remove(adapterPosition);
            mainAdapter.notifyItemRemoved(adapterPosition);
        }
    }

    @Override
    public boolean onMove(int srcPosition, int targetPosition) {
        if (userInfoList != null) {
            // 更換數據源中的數據Item的位置
            Collections.swap(userInfoList, srcPosition, targetPosition);
            // 更新UI中的Item的位置,主要是給用戶看到交互效果
            mainAdapter.notifyItemMoved(srcPosition, targetPosition);
            return true;
        }
        return false;
    }
};

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved