RecyclerView 使用ItemTouchHelper實現滑動刪除、拖移改變順序效果

前置作業創建RecyclerView、Adapter我這邊就不贅述了

直接進入主題

我們會使用到ItemTouchHelper工具類別
官方解釋:這是一個實用工具類,用於添加滑動以關閉和拖放支持到RecyclerView。
這是一個支持RecyclerView滑動刪除和拖拽的實用工具類

如何使用:

SimpleCallback建立時,需要指定想要支援的拖拉(move)與swipe的方向,在此範例我們讓他可以上下拖拉跟左右swipe(或用START、END,則可支援右到左方向的排列), 而主要需要實作的函式有分別對應到拖拉與swipe的onMoveonSwipe
private void setUpRecyclerView() {
        adapter = new ContactAdapter(options);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);

        //  實現拖移、左右滑動刪除的效果
        new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
                ItemTouchHelper.UP | ItemTouchHelper.DOWN,
                ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
                //  上下拖移callback
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                // 左右滑動callback
            }
        }).attachToRecyclerView(recyclerView);
    }
做完以上動作,RecyclerView已經有上下拖拉跟左右swipe效果了,如下面圖示。

但這一步驟所做的只是讓單一列表項目有被拖拉跟swipe的效果,其他的列表項目並沒有跟著移動。

加入拖拉時其他項目的效果與資料更新

@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                      RecyclerView.ViewHolder target) {
    // Step 2-1
    final int fromPos = viewHolder.getAdapterPosition();
    final int toPos = target.getAdapterPosition();
    // move item in `fromPos` to `toPos` in adapter.
    adapter.notifyItemMoved(fromPos, toPos);
    return true;// true if moved, false otherwise
}
做完以上動作,拖拉項目時就能有如下圖的效果,可以看到其他項目會跟著移動,且放開項目之後,該項目真的會停留在目標位置。
不過上面的動作只是畫面做了更動,實際上列表的資料Model並沒有真的做了移動,所以在拖放之後,如果滾動列表,會發現列表項目又恢復移動前的排列位置。

加入資料Model更新的程式碼之後,整個程式碼大致如下面兩個區塊, 在此範例中,我將資料更新與通知列表更新的動作放在自行實作的Adapter的public函式moveItem()
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                      RecyclerView.ViewHolder target) {
    // Step 2-2
    final int fromPos = viewHolder.getAdapterPosition();
    final int toPos = target.getAdapterPosition();
    // move item in `fromPos` to `toPos` in adapter.
    adapter.moveItem(fromPos, toPos);
    return true;// true if moved, false otherwise
}
moveItem()的實作如下,items是範例中的資料Model,型態是List,在此我用Collections.swap()做個簡單的項目交換, 接著一樣用notifyItemRemoved()更新畫面。
// Step 2-2
public void moveItem(int fromPos, int toPos) {
    Collections.swap(items, fromPos, toPos);
    adapter.notifyItemMoved(fromPos, toPos);
}


Swipe之後的動畫效果與資料更新

在此範例中,我們以刪除作為swipe時要執行的目標動作, 首先,在onSwiped的函式中加入以下程式碼,就能讓列表有其他項目自動上移的效果, 主要作法是透過viewHolder取得目前swipe的項目位置,然後利用Adapter內建的notifyItemRemoved(int position)通知列表畫面更新。
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    // Step 3-1
    int position = viewHolder.getAdapterPosition();
    adapter.notifyItemRemoved(position);
}

效果如下:

同上下拖拉一樣,目前的程式碼也只是讓畫面有效果,但還沒有連同資料model一起更新,所以會發現畫面上的列表下方好像怪怪的。 所以需要再調整成如下的程式碼,在此我將刪除項目的動作放在自行實作的Adapter的public函式removeItem()
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    // Step 3-2
    int position = viewHolder.getAdapterPosition();
    adapter.removeItem(position);
}
removeItem()的實作如下,主要內容是從model中移除指定的項目並通知畫面更新。
// Step 3-2
public void removeItem(int position) {
    items.remove(position);
    adapter.notifyItemRemoved(position);
}

最後效果如下圖:



留言

這個網誌中的熱門文章

Android - 使用 adb 安装apk

Android ContentProvider 實現多個應用程式共享資料

Android TextView autosizing 自動調整大小