
在第一部分中,我们研究了ItemTouchHelper和ItemTouchHelper.Callback的实现,该实现向RecyclerView添加了基本的拖放和滑动到关闭功能 。 在本文中,我们将通过添加对以网格形式布置元素,拖放控制器,列表项的选择以及自定义滑动动画的支持,继续上一版本的工作。

拖放控制器
创建支持拖放的列表时,它们通常实现通过触摸来拖放项目的功能。 这有助于“编辑模式”中列表的清晰性和易用性,并且也由材料准则建议 。 在我们的示例中添加拖放控制器非常简单。

首先,更新元素的layout
( item_main.xml )。
<?xml version="1.0" encoding="utf-8"?> <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图标中找到用于拖动控制器的图像,并使用Android Studio中方便的图标生成器插件将其添加到项目中。
如前一篇文章中简要提到的,您可以使用ItemTouchHelper.startDrag(ViewHolder)
以编程方式开始拖放操作。 因此,我们要做的就是通过添加拖动控制器来更新ViewHolder
并设置一个简单的触摸处理程序,该处理程序将调用startDrag()
。
我们将需要一个接口来沿链传输事件:
public interface OnStartDragListener { void onStartDrag(RecyclerView.ViewHolder viewHolder); }
然后在ItemViewHolder
为拖动控制器定义一个ImageView
:
public final ImageView handleView; public ItemViewHolder(View itemView) { super(itemView);
并更新RecyclerListAdapter
:
private final OnStartDragListener mDragStartListener; public RecyclerListAdapter(OnStartDragListener dragStartListener) { mDragStartListener = dragStartListener;
RecyclerListAdapter
现在应该看起来像这样 。
剩下要做的就是将OnStartDragListener
添加到片段中:
public class RecyclerListFragment extends Fragment implements OnStartDragListener {
RecyclerListFragment
现在应该看起来像这样 。 现在,当您启动应用程序时,可以通过触摸控制器开始拖放。

突出显示列表项
现在,在我们的示例中,没有视觉上的指示表明正在拖动该项目。 显然,这不是应该的,但是很容易解决。 使用ItemTouchHelper
可以使用标准项目突出显示效果。 在Lollipop和更高版本的Android上,背光在与元素交互的过程中“散布”在元素上; 在早期版本中,该元素只是将其颜色更改为深色。
要在我们的示例中实现此目的,只需FrameLayout
元素的根FrameLayout
添加背景( background
属性),或在RecyclerListAdapter.ItemViewHolder的构造函数中进行设置。 它看起来像这样:

它看起来很酷,但是您可能想要控制更多。 一种方法是允许ViewHolder
处理元素的状态更改。 为此, ItemTouchHelper.Callback
提供了另外两种方法:
- 每当元素的状态更改为拖动 ( ACTION_STATE_DRAG )或滑动 ( ACTION_STATE_SWIPE )时,都会调用
onSelectedChanged(ViewHolder, int)
)。 这是将view
组件的状态更改为active的理想位置。 - 拖放
view
组件完成时,以及滑动完成时clearView(RecyclerView, ViewHolder)
调用clearView(RecyclerView, ViewHolder)
( ACTION_STATE_IDLE )。 在这里,通常会恢复view
组件的初始状态。
现在,让我们将它们放在一起。
首先,创建一个ViewHolders
将实现的接口:
public interface ItemTouchHelperViewHolder { void onItemSelected(); void onItemClear(); }
然后,在SimpleItemTouchHelperCallback
实现适当的方法:
@Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
现在剩下的就是RecyclerListAdapter.ItemViewHolder
实现ItemTouchHelperViewHolder
:
public class ItemViewHolder extends RecyclerView.ViewHolder implements ItemTouchHelperViewHolder {
在此示例中,我们仅在元素处于活动状态时添加灰色背景,然后将其删除。 如果您的ItemTouchHelper
和适配器紧密相连,则可以轻松地放弃此设置,而直接在ItemTouchHelper.Callback
切换view
组件的状态。
篮网
如果现在尝试使用GridLayoutManager
,则会看到它无法正常工作。 原因和解决方案很简单:我们必须告诉ItemTouchHelper
我们希望支持左右拖动项目。 之前在SimpleItemTouchHelperCallback
我们已经指定了:
@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); }
支持网格的唯一更改是添加适当的标志:
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
但是,对于网格元素,“ 轻扫以消除”并不是很自然的行为,因此swipeFlags
最有可能被swipeFlags
:
@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); }
要查看GridLayoutManager
的工作示例,请参阅RecyclerGridFragment 。 这是启动时的样子:

自定义滑动动画
ItemTouchHelper.Callback
提供了一种非常方便的方法,可以在拖动或扫动时完全控制动画。 由于ItemTouchHelper
是RecyclerView.ItemDecoration ,因此我们可以以类似方式干预呈现view
组件的过程。 在下一部分中,我们将更详细地研究这个问题,但是现在,让我们看一个覆盖默认滑动动画以显示线性消失的简单示例。
@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); } }
dX
和dY
是相对于所选view
组件的当前偏移,其中:
- -1.0f是从右到左的完整滑动(从
ItemTouchHelper.END
到ItemTouchHelper.START
) - 从左到右(从
ItemTouchHelper.START
到ItemTouchHelper.END
)完全滑动1.0f。
重要的是要为actionState
的任何actionState
调用super
来触发默认动画。
在下一部分中,我们将考虑一个示例,在该示例中,我们将在拖放时控制元素的绘制。
结论
实际上,设置ItemTouchHelper很有趣。 为了不增加本文的数量,我将其分为几部分。
源代码
请参阅Android-ItemTouchHelper-Demo GitHub存储库上的本系列文章的完整代码。 本文介绍了从ef8f149到d164fba的提交。
←在RecyclerView中拖动和滑动。 第1部分:ItemTouchHelper