将滑动手势添加到RecyclerViews

Material Design的很大一部分是用户与应用程序的视觉元素进行交互的方式。 因此,除了水龙头和长按以外,今天,一款精巧的Android应用程序有望处理更复杂的触摸手势,例如滑动和拖动。 如果应用程序使用列表来显示其数据,这尤其重要。

通过使用RecyclerView小部件和其他一些Android Jetpack组件,您可以处理应用程序中与列表相关的多种滑动手势。 此外,只需几行代码,就可以将Material Motion动画与这些手势相关联。

在本教程中,我将向您展示如何向列表中添加一些常见的滑动手势以及直观的动画。

先决条件

为了充分利用本教程,您需要:

  • Android Studio 3.2.1或更高版本
  • 运行Android API级别23或更高版本的手机或平板电脑

1.创建一个列表

为了使本教程简短,让我们使用Android Studio中可用的模板之一来生成列表。

首先启动Android Studio并创建一个新项目。 在项目创建向导中,确保选择“ 清空活动”选项。

Project creation wizard

在该项目中,我们将使用Android Jetpack代替Support库。 因此,一旦生成了项目,请转到Refactor> Migrate to AndroidX 。 出现提示时,按“ 迁移”按钮。

确认提示以继续进行迁移

接下来,要将列表添加到项目中,请转到“ 文件”>“新建”>“片段”>“片段(列表)” 。 在弹出的对话框中,继续并按“ 完成”按钮,而不对默认值进行任何更改。

列表片段创建向导

此时,Android Studio将创建一个包含完整配置的RecyclerView小部件的新片段。 它还将生成虚拟数据以显示在小部件内。 但是,您仍然必须手动将片段添加到您的主要活动中。

为此,首先将OnListFragmentInteractionListener接口添加到您的主要活动中,并实现其包含的唯一方法。

override fun onListFragmentInteraction(
                            item: DummyContent.DummyItem?) {
   // leave empty 
}

接下来,通过将以下<fragment>标记添加到activity_main.xml文件,将片段嵌入活动中

<fragment android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:id="@+id/list_fragment"
          android:name="com.tutsplus.rvswipes.ItemFragment" />

此时,如果您运行应用程序,则应该能够看到如下所示的列表:

应用显示简单列表

2.添加要删除的手势

使用ItemTouchHelper类,可以快速向任何RecyclerView小部件添加滑动和拖动手势。 该类还提供默认动画,只要检测到有效手势,它们就会自动运行。

ItemTouchHelper类需要抽象ItemTouchHelper.Callback类的实例才能检测和处理手势。 尽管您可以直接使用它,但是使用名为SimpleCallback的包装器类要容易得多。 它也是抽象的,但是您将拥有更少的重写方法。

ItemFragment类的onCreateView()方法内创建SimpleCallback类的新实例。 作为其构造函数的参数,必须传递要处理的滑动方向。 现在,将RIGHT传递给它,以便它处理向右滑动的手势。

val myCallback = object: ItemTouchHelper.SimpleCallback(0, 
                                 ItemTouchHelper.RIGHT) {
                                 
    // More code here
    
}

该类有两个抽象方法,您必须重写它们: onMove()方法(用于检测拖动)和onSwiped()方法(用于检测滑动)。 由于我们今天将不会处理任何拖动手势,因此请确保在onMove()方法内返回false

override fun onMove(
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    target: RecyclerView.ViewHolder
): Boolean = false

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, 
                      direction: Int) {

    // More code here

}

onSwiped()方法内部,可以使用adapterPosition属性来确定已刷过的列表项的索引。 因为我们现在正在实现“滑动到移除”手势,所以将索引传递给虚拟列表的removeAt()方法以移除项目。

DummyContent.ITEMS.removeAt(viewHolder.adapterPosition)

此外,必须将相同的索引传递给RecyclerView小部件的适配器的notifyItemRemoved()方法,以确保不再渲染该项目。 这样做还会运行默认的项目删除动画。

adapter?.notifyItemRemoved(viewHolder.adapterPosition)

至此, SimpleCallback对象已准备就绪。 现在您需要做的就是使用它创建一个ItemTouchHelper对象,并将RecyclerView小部件附加到该对象。

val myHelper = ItemTouchHelper(myCallback)
myHelper.attachToRecyclerView(this)

如果您现在运行该应用程序,则可以从列表中滑动项目。

3.显示背景视图

尽管轻扫即可删除手势非常直观,但是某些用户可能不确定执行该手势时会发生什么。 因此,材料设计指南指出,手势还必须逐步显示隐藏在项目后面的视图,该视图清楚地表明下一步将发生什么。 通常,背景视图只是显示垃圾箱的图标。

要将垃圾桶图标添加到项目中,请转到文件>新建>矢量资产,然后选择名为delete的图标。

图标选择对话框

现在,您可以通过调用getDrawable()方法在Kotlin代码中获得对该图标的引用。 因此, ItemFragment添加到ItemFragment类的onCreateView()方法:

val trashBinIcon = resources.getDrawable(
                        R.drawable.ic_delete_black_24dp, 
                        null
                   )

在列表项后面显示视图有点复杂,因为您需要手动绘制它,同时还要确保其边界与逐渐显示的区域的边界相匹配。

重写您的SimpleCallback实现的onChildDraw()方法以开始绘制。

override fun onChildDraw(
    c: Canvas,
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    dX: Float,
    dY: Float,
    actionState: Int,
    isCurrentlyActive: Boolean
) {
    
    // More code here
    
    super.onChildDraw(c, recyclerView, viewHolder, 
                dX, dY, actionState, isCurrentlyActive)
}

在上面的代码中,对超类的onChildDraw()方法的调用很重要。 没有它,您的列表项在滑动时将不会移动。

由于我们仅处理向右滑动手势,因此背景视图的左上角和左下角的X坐标将始终为零。 另一方面,右上角和右下角的X坐标应等于dX参数,该参数指示用户已将列表项移动了多少。

要确定所有角的Y坐标,您必须使用viewHolder对象内部存在的视图之一的topbottom属性。

使用所有这些坐标,您现在可以定义一个矩形剪辑区域。 以下代码显示了如何使用Canvas对象的clipRect()方法:

c.clipRect(0f, viewHolder.itemView.top.toFloat(),
        dX, viewHolder.itemView.bottom.toFloat())

尽管不必这样做,但最好给剪辑区域提供背景色以使其可见。 这是您可以使用drawColor()方法在滑动距离较小时使剪辑区域变为灰色,在滑动距离较大时使剪辑区域变为红色的方法。

if(dX < width / 3)
    c.drawColor(Color.GRAY)
else
    c.drawColor(Color.RED)

现在,您必须指定垃圾桶图标的范围。 这些边界必须包含与列表项中显示的文本匹配的边距。 要确定以像素为单位的边距值,请使用getDimension()方法并将text_margin传递给它。

val textMargin = resources.getDimension(R.dimen.text_margin)
                          .roundToInt()

您可以将剪辑区域左上角的坐标重用为图标左上角的坐标。 但是,它们必须由textMargin偏移。 要确定其右下角的坐标,请使用其固有宽度和高度。 这是如何做:

trashBinIcon.bounds = Rect(
    textMargin, 
    viewHolder.itemView.top + textMargin,
    textMargin + trashBinIcon.intrinsicWidth, 
    viewHolder.itemView.top + trashBinIcon.intrinsicHeight 
                            + textMargin
)

最后,通过调用其draw()方法来绘制图标。

trashBinIcon.draw(c)

如果再次运行该应用程序,则在滑动以删除列表项时应该能够看到该图标。

4.添加刷卡刷新手势

滑动刷新手势(也称为拉动刷新手势)近来非常流行,以至于Android Jetpack都有专用的组件。 它称为SwipeRefreshLayout ,它使您可以快速将手势与任何RecyclerViewListViewGridView小部件关联。

要在RecyclerView小部件中支持刷卡刷新手势,必须使其成为SwipeRefreshLayout小部件的SwipeRefreshLayout 。 因此,打开fragment_item_list.xml文件,向其中添加一个<SwipeRefreshLayout>标记,然后在其中移动<RecyclerView>标记。 之后,文件的内容应如下所示:

<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        xmlns:android="https://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/list"
            android:name="com.tutsplus.rvswipes.ItemFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"/>

</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

该列表片段假定RecyclerView小部件是其布局的根元素。 因为这不再成立,所以您需要在ItemFragment类的onCreateView()方法中进行一些更改。 首先,用下面的代码替换方法的第一行,该方法使布局更加膨胀:

val srLayout: SwipeRefreshLayout = 
            inflater.inflate(
               R.layout.fragment_item_list, container, false
            ) as SwipeRefreshLayout

val view = srLayout.findViewById<RecyclerView>(R.id.list)

接下来,更改方法的最后一行以返回SwipeRefreshLayout小部件,而不是RecyclerView小部件。

return srLayout

如果您现在尝试运行该应用程序,则可以执行垂直滑动手势并获得视觉反馈。 不过,列表的内容不会改变。 要实际刷新列表,必须将OnRefreshListener对象与SwipeRefreshLayout小部件关联。

srLayout.setOnRefreshListener {
    // More code here    
}

在侦听器内部,您可以根据需要自由修改列表显示的数据。 现在,因为我们正在使用虚拟数据,所以我们只清空虚拟项目列表,并重新加载25个新的虚拟项目。 以下代码显示了如何执行此操作:

DummyContent.ITEMS.clear()
for(i in 1..25) {
    DummyContent.ITEMS.add(
        DummyContent.DummyItem("$i", "Item $i", "")
    )
}

更新数据后,您必须记住要调用notifyDataSetChanged()方法,以使RecyclerView小部件的adapter知道它必须重绘该列表。

view.adapter?.notifyDataSetChanged()

默认情况下,一旦用户执行从SwipeRefreshLayout到刷新的手势, SwipeRefreshLayout小部件就会显示动画进度指示器。 因此,在更新列表之后,必须记住要通过将小部件的isRefreshing属性设置为false来删除指示器。

srLayout.isRefreshing = false

如果您现在运行该应用程序,请删除一些列表项,然后执行“轻拂刷新”手势,列表会自动重置。

结论

Material Design已经存在了几年,如今,大多数用户希望您能够处理它提到的许多手势。 幸运的是,这样做不需要太多的努力。 在本教程中,您学习了如何实现两种非常常见的滑动手势。 您还学习了如何逐步显示隐藏在要滑动的列表项后面的视图。

您可以通过参考“ 手势设计”指南来了解有关手势和材质运动的更多信息。

翻译自: https://code.tutsplus.com/tutorials/adding-swipe-gestures-to-recyclerviews--cms-32427

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值