Android上面有許多的教程, 庫和示例, 在RecyclerView上面實現"拖放"和"滑動刪除"功能. 儘管有更新, 更好的方法可用, 可是大多數人依然使用舊的View.OnDragListener和Roman Nurik的SwipeToDismiss方式. 除了常常使用GestureDetector和onInterceptTouchEvent以外, 幾乎不多有人使用新的API, 要否則的話, 實現就複雜. 事實上真的有十分簡單的方式在RecyclerView上面添加這兩個功能. 它只要求一個類, 並且這個類已是Android支持包的一部分.
html
ItemTouchHelperandroid
ItemTouchHelper是一個強大的通用程序, 在RecyclerView上面添加"拖放"和"滑動刪除"時, 你所須要作的全部事情, 它都會負責處理. 它是RecyclerView.ItemDecoration的子類, 這意味着它能夠輕易地添加到任何已經存在的LayoutManager和Adapter上面! 它不會影響添加到item上的動畫, 而且支持類別嚴格的"拖", 以及"放"時的動畫, 還能夠支持更多. git
準備:github
首先, 咱們所須要的是添加RecyclerView的依賴: ide
1 compile 'com.android.support:recyclerview-v7:25.3.0'
使用ItemTouchHelper和ItemTouchHelper.Callback:學習
爲了使用ItemTouchHelper, 你將建立一個ItemTouchHelper.Callback, 這是一個接口, 容許你監聽"move"和"swipe"事件, 並且你能夠經過Callback來控件已選中view的狀態, 而且能夠改變該view的默認動畫. 若是隻是想要一個基礎實現, 你可使用SimpleCallback這個幫助類, 可是爲了學習Callback的工做原理, 咱們將會本身實現一個.動畫
爲了激活基本的"拖放"和"滑動刪除", 咱們必須覆蓋的主要方法是:ui
getMovementFlags(RecyclerView, ViewHolder) onMove(RecyclerView, ViewHolder, ViewHolder) onSwiped(ViewHolder, int)
咱們也要使用這兩個方法:spa
isLongPressDragEnabled()
isItemViewSwipeEnabled()
咱們一個一個地看一下: code
@Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); }
ItemTouchHelper容許你輕易地決定事件的方向.你必須實現getMovementFlags(RecyclerView, RecyclerView.ViewHolder)方法來指明"拖"和"滑動"所支持的方向, 而且使用ItemTouchHelper.makeMovementFlags(int, int)來構建返回標籤. 在此咱們在兩個不一樣的方向激活"拖"和"滑動".
@Override public boolean isLongPressDragEnabled() { return true; }
ItemTouchHelper可以用來實現"沒有滑動的拖動"或者"沒有拖動的滑動", 因此你必須精確地指明想要支持的動做. 若是你想要在RecyclerView的item上支持"長按啓動拖放"事件, 你就必須實現isLongPressDragEnabled()返回true. 此外, ItemTouchHelper.startDrag(RecyclerView.ViewHolder)能夠從"操做"中啓動"拖放", 這一點會在以後詳述.
@Override public boolean isItemViewSwipeEnabled() { return true; }
要想要view內部的任意觸摸事件均可以啓動"滑動"動做, 就簡單地在isItemViewSwipeEnabled()返回true. 此外, ItemTouchHelper.startSwipe(RecyclerView.ViewHolder)可以手動地啓動"滑動"事件.
而後, onMove()和onSwiped()方法須要實現, 來通知負責更新基礎數據的東西. 因此, 首先, 咱們要建立一個接口, 以容許咱們傳遞"拖放"和"滑動刪除"事件的回調.
public interface ItemTouchHelperAdapter { void onItemMove(int fromPosition, int toPosition); void onItemDismiss(int position); }
從當前示例來說, 要實現這些的最簡單的方式, 是將咱們的RecyclerView.Adapter實現這個接口:
public class RecyclerListAdapter extends RecyclerView.Adapter<ItemViewHolder> implements ItemTouchHelperAdapter { // ... code from gist @Override public void onItemDismiss(int position) { mItems.remove(position); notifyItemRemoved(position); } @Override public boolean onItemMove(int fromPosition, int toPosition) { if (fromPosition < toPosition) { for (int i = fromPosition; i < toPosition; i++) { Collections.swap(mItems, i, i + 1); } } else { for (int i = fromPosition; i > toPosition; i--) { Collections.swap(mItems, i, i - 1); } } notifyItemMoved(fromPosition, toPosition); return true; }
調用notifyItemRemoved(int)和notifyItemMoved(int, int)是很是重要的, 由此, Adapter會更新數據. 請注意, 這也很重要, 咱們改變item的position是在每一次view被切換到新的index, 而不是在"放"事件以後.
如今咱們回來構建SimpleItemTouchHelperCallback, 由於咱們依然必須覆蓋onMove()和onSwiped()方法. 首先, 爲Adapter添加構建器和變量:
private final ItemTouchHelperAdapter mAdapter; public SimpleItemTouchHelperCallback( ItemTouchHelperAdapter adapter) { mAdapter = adapter; }
而後覆蓋剩下的事件並通知Adapter:
@Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); return true; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); }
這個Callback應該看起來像這樣:
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { private final ItemTouchHelperAdapter mAdapter; public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) { mAdapter = adapter; } @Override public boolean isLongPressDragEnabled() { return true; } @Override public boolean isItemViewSwipeEnabled() { return true; } @Override public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; return makeMovementFlags(dragFlags, swipeFlags); } @Override public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target) { mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition()); return true; } @Override public void onSwiped(ViewHolder viewHolder, int direction) { mAdapter.onItemDismiss(viewHolder.getAdapterPosition()); } }
當Callback準備好以後, 咱們建立ItemTouchHelper並調用attachToRecyclerView(RecyclerView)方法:
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter); ItemTouchHelper touchHelper = new ItemTouchHelper(callback); touchHelper.attachToRecyclerView(recyclerView);
當你運行的時候, 結果應該看起來像這樣:
總結
這是一個ItemTouchHelper極簡單的實現. 可是咱們應該清楚, 在RecyclerView上面實現基本的"拖放"和"滑動刪除", 使用第三方和庫是徹底沒有必要的.