Android開發(fā)在RecyclerView上面實現(xiàn)"拖放"和"滑動刪除"-2
上篇給大家介紹了Android一步步帶你在RecyclerView上面實現(xiàn)"拖放"和"滑動刪除"功能
效果如下:

拖動手柄
在設(shè)計一個支持"拖放"的列表時, 通常提供一個在觸摸時初始化拖拽的"拖動手柄". 因其可發(fā)現(xiàn)性和可用性而被Material Guidelines所推薦, 尤其是列表處于"可編輯模式"時.

首先更新item的布局(item_main.xml):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/item" android:layout_width="match_parent" android:layout_height="?listPreferredItemHeight" android:clickable="true" android:focusable="true" android:foreground="?selectableItemBackground"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="16dp" android:textAppearance="?android:attr/textAppearanceMedium" /> <ImageView android:id="@+id/handle" android:layout_width="?listPreferredItemHeight" android:layout_height="match_parent" android:layout_gravity="center_vertical|right" android:scaleType="center" android:src="@drawable/ic_reorder_grey_500_24dp" /> </FrameLayout>
用作"拖放手柄"的圖片可以在Material Design Icon找到, 也可以方便地通過Android Material Design Icon Generator Plugin添加.
我們曾經(jīng)提到過, 可以通過代碼ItemTouchHelper.startDrag(RecyclerView.ViewHolder)來開啟拖動. 所以我們要做的就是更新ViewHolder來包含新的手柄視圖, 并設(shè)置一個簡單的觸摸事件接口, 以觸發(fā)startDrag()方法.
我們需要定義一個接口來傳遞拖動事件.
public interface OnStartDragListener {
/**
* Called when a view is requesting a start of a drag.
*
* @param viewHolder The holder of the view to drag.
*/
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
然后, 在ItemViewHolder中實現(xiàn)化手柄視圖.
public final ImageView handleView;
public ItemViewHolder(View itemView) {
super(itemView);
// ...
handleView = (ImageView) itemView.findViewById(R.id.handle);
}
并且更新Adapter.
private final OnStartDragListener mDragStartListener;
public RecyclerListAdapter(OnStartDragListener dragStartListener) {
mDragStartListener = dragStartListener;
// ...
}
@Override
public void onBindViewHolder(final ItemViewHolder holder,
int position) {
// ...
holder.handleView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) ==
MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
}
return false;
}
});
}
完整的Adapter應(yīng)該看起來像這個.
剩下的是把OnStartDragListener添加到Fragment.
public class RecyclerListFragment extends Fragment implements
OnStartDragListener {
// ...
@Override
public void onViewCreated(View view, Bundle icicle) {
super.onViewCreated(view, icicle);
RecyclerListAdapter a = new RecyclerListAdapter(this);
// ...
}
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
mItemTouchHelper.startDrag(viewHolder);
}
}
但你運行之后, 可以看到這樣的效果:

標(biāo)示選中視圖
在上一篇的基礎(chǔ)示例中, 被拖拽的item事實上是被選中的, 但是沒有可視化的標(biāo)示. 由于顯著的理由, 這是不受歡迎的, 但也很容易修復(fù). 事實上, 在ItemTouchHelper的幫助下, 只要你的ViewHolder的itemView設(shè)置了background集合(selector), 就會得到相應(yīng)的效果. 在Lollipop及之后的版本, item view的elevation在拖拽和滑動期間會增加. 而在之前的版本中, 滑動時會有fade效果. 看起來就像:

效果看起來不錯, 但你可能想要更多的控制. 一種方法是, 無論任何時候ViewHolder被選中或者清空, 讓item自己處理這種改變. 由此, ItemTouchHelper.Callback提供了兩種回調(diào).
onSelectedChanged(RecyclerView.ViewHolder, int). 每一次ViewHolder的狀態(tài), 變成drag(ACTION_STATE_DRAG)或者swipe(ACTION_STATE_SWIPE)時, 該方法就會被調(diào)用. 這時候是將ViewHolder的狀態(tài)變成active的完美時刻.
clearView(RecyclerView, RecyclerView.ViewHolder). 在被拖拽的ViewHolder放下時, 或者是滑動操作取消或者完成時(ACTION_STATE_IDLE), 這里會是將ItemView狀態(tài)設(shè)置為idle的最好的地方.
那么, 我們就把兩者綁定在一起實現(xiàn).
首先, 創(chuàng)建一個接口, 讓目標(biāo)ViewHolder實現(xiàn):
/**
* Notifies a View Holder of relevant callbacks from
* {@link ItemTouchHelper.Callback}.
*/
public interface ItemTouchHelperViewHolder {
/**
* Called when the {@link ItemTouchHelper} first registers an
* item as being moved or swiped.
* Implementations should update the item view to indicate
* it's active state.
*/
void onItemSelected();
/**
* Called when the {@link ItemTouchHelper} has completed the
* move or swipe, and the active item state should be cleared.
*/
void onItemClear();
}
接著, 觸發(fā)相應(yīng)的回調(diào)方法:
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
int actionState) {
// We only want the active item
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder) {
ItemTouchHelperViewHolder itemViewHolder =
(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
ItemTouchHelperViewHolder itemViewHolder =
(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
現(xiàn)在, 剩下的是讓ItemViewHolder實現(xiàn)ItemTouchHelperViewHolder:
public class ItemViewHolder extends RecyclerView.ViewHolder
implements ItemTouchHelperViewHolder {
// ...
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
考慮到這僅僅是一個示例, 我們僅僅是在item處于active狀態(tài)時設(shè)置了灰色背景, 當(dāng)item被清空時, 把灰色背景刪除了. 如果你的Adapter和ItemTouchHelper緊密結(jié)對的話, 輕易地放棄這個設(shè)置也行, 轉(zhuǎn)而可以直接在ItemTouchHelper.Callback轉(zhuǎn)變item的狀態(tài).
Grid布局
如果你想在本項目中轉(zhuǎn)而使用GridLayoutManager, 你會發(fā)現(xiàn)并沒有正常地起作用. 原因以及修復(fù)原因都很簡單: 必須告訴ItemTouchHelper我們想要支持左右拖拽. 在SimpleTouchHelperCallback中, 我們已經(jīng)聲明:
@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);
}
支持Grid布局所要求的唯一改變是dragFlags中添加左右方向:
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
但是, 對于grid而言, "滑動刪除"并不是一個天然的功能模式, 所以你也許會寫一些如下的代碼:
@Override
public int getMovementFlags(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
Grid上面的上下左右"拖放"效果看起來如下:

自定義滑動動畫
對我們而言, 在viewHolder拖拽和滑動的時候, ItemTouchHelper.Callback為我們提供了一個十分方便的方式來完全控制ViewHolder的動畫. 因為ItemTouchHelper是一個ItemDecoration, 我們可以用類似的方式, 詳細(xì)地了解視圖的繪制(hook into the View drawing).
下面是一個簡單的例子, 來覆蓋默認(rèn)的滑動動畫, 來展示一個線性的fade效果.
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
ViewHolder viewHolder, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
float width = (float) viewHolder.itemView.getWidth();
float alpha = 1.0f - Math.abs(dX) / width;
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY,
actionState, isCurrentlyActive);
}
}
參數(shù)dX和dY代表選中視圖的當(dāng)前位置,
- -1.0f表示完完全全的從ItemTouchHelper.END到ItemTouchHelper.START的滑動.
- 1.0f表示完完全全的從ItemTouchHelper.START到ItemTouchHelper.END的滑動.
對于任何沒有處理的actionState你都可以調(diào)用super的方法, 從而使用默認(rèn)的動畫.
總結(jié)
我們僅僅了解了自定義ItemTouchHelper所能做的事情中有趣的部分, 我希望能夠在一篇文章中包含更多內(nèi)容, 但是考慮到文章長度, 還是分開比較好.
以上所述是小編給大家介紹的Android開發(fā)在RecyclerView上面實現(xiàn)"拖放"和"滑動刪除功能(二),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
Ionic2創(chuàng)建App啟動頁左右滑動歡迎界面
使用Ionic2創(chuàng)建應(yīng)用非常簡單,只需在V1的命令后跟上--v2即可.這篇文章主要介紹了Ionic2創(chuàng)建App啟動頁左右滑動歡迎界面的相關(guān)資料,需要的朋友可以參考下2016-10-10
Android?studio開發(fā)實現(xiàn)計算器功能
這篇文章主要為大家詳細(xì)介紹了Android?studio開發(fā)實現(xiàn)計算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05
Android Support Annotations資料整理
這篇文章主要介紹了Android Support Annotations資料整理的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android仿淘寶view滑動至屏幕頂部會一直停留在頂部的位置
這篇文章主要介紹了Android仿淘寶view滑動至屏幕頂部會一直停留在頂部的位置的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-11-11
RxJava+Retrofit+OkHttp實現(xiàn)文件上傳
本篇文章主要介紹了RxJava+Retrofit+OkHttp實現(xiàn)文件上傳,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11
Android處理圖像數(shù)據(jù)轉(zhuǎn)換的各種方法
這篇文章主要介紹了Android處理圖像數(shù)據(jù)轉(zhuǎn)換的各種方法,本文講解了RGB值轉(zhuǎn)Bitmap、Color值轉(zhuǎn)Bitmap、字節(jié)數(shù)組轉(zhuǎn)Bitmap、讀取文件轉(zhuǎn)Bitmap、讀取資源轉(zhuǎn)Bitmap、輸入流轉(zhuǎn)Bitmap等內(nèi)容,需要的朋友可以參考下2015-01-01

