欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

如何将多个选择添加到Android RecyclerView

程序员文章站 2022-05-31 20:25:48
...

RecyclerView小部件是当今大多数Android应用程序不可或缺的一部分。 自从2014年末将其添加到Android支持库以来,它已经使ListView小部件黯然失色,成为显示大型复杂列表的首选小部件。 但是,它缺少一个重要功能:支持选择和跟踪列表项。 Google在今年3月发布的附加库RecyclerView Selection试图解决此问题。

在本教程中,我将向您展示如何使用新库来创建一个应用程序,该应用程序提供了用于选择列表中多个项目的直观界面。 跟随这个Android RecyclerView多项选择示例,您将学到一些可以在自己的应用中应用的技能。

先决条件

要继续进行,您需要:

  • 最新版本的Android Studio
  • 运行Android API级别23或更高版本的设备或模拟器

要将RecyclerView Selection库添加到您的Android Studio项目中,请在app模块的build.gradle文件中提及以下implementation依赖

implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:recyclerview-selection:28.0.0'

在整个教程中,我们将处理一小部分项目,每个项目都包含一个人的姓名和电话号码。

要存储每个列表项的数据,请创建一个名为Person的Kotlin数据类,并向其中添加两个属性: namephone

data class Person(val name:String,
                  val phone: String)

现在,您可以继续在主要活动中创建Person对象的列表。

val myList = listOf(
    Person("Alice", "555-0111"),
    Person("Bob", "555-0119"),
    Person("Carol", "555-0141"),
    Person("Dan", "555-0155"),
    Person("Eric", "555-0180"),
    Person("Craig", "555-0145")
)

当然,我们将使用RecyclerView小部件来显示列表。 因此,在主活动的布局XML文件中添加一个<RecyclerView>标记。

<android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/my_rv">

</android.support.v7.widget.RecyclerView>

要指定列表项的布局,请创建一个新的XML文件并将其命名为list_item.xml 在其中添加两个TextView小部件:一个用于显示名称,另一个用于显示电话号码。 如果使用LinearLayout元素定位小部件,则XML文件的内容应如下所示:

<LinearLayout 
    xmlns:android="https://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/list_item_name"
        style="@style/TextAppearance.AppCompat.Large"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/list_item_phone"
        style="@style/TextAppearance.AppCompat.Small"/>
        
</LinearLayout>

您可以将视图持有者视为一个对象,其中包含对列表项布局中存在的视图的引用。 没有它, RecyclerView小部件将无法有效地呈现列表项。

现在,您需要一个视图保持器,其中包含您在上一步中创建的两个TextView小部件。 因此,创建一个扩展RecyclerView.ViewHolder类的新类,并初始化对其中两个小部件的引用。 这是如何做:

class MyViewHolder(view: View)
    : RecyclerView.ViewHolder(view) {

    val name: TextView = view.list_item_name
    val phone: TextView = view.list_item_phone

    // More code here
    
}

此外, RecyclerView Selection插件需要一种可以调用的方法来唯一地标识选定的列表项。 理想情况下,此方法属于视图持有者本身。 此外,它必须返回ItemDetailsLookup.ItemDetails类的实例。 因此,将以下代码添加到视图持有人:

fun getItemDetails(): ItemDetailsLookup.ItemDetails<Long> =
        object: ItemDetailsLookup.ItemDetails<Long>() {

            // More code here

        }

现在,您必须重写ItemDetails类中存在的两个抽象方法。 首先,重写getPosition()方法并返回其中的视图持有者的adapterPosition属性。 adapterPosition属性通常只是列表项的索引。

override fun getPosition(): Int = adapterPosition

接下来,重写getSelectionKey()方法。 此方法必须返回可用于唯一标识列表项的键。 为了简单itemId ,我们只返回视图持有者的itemId属性。

override fun getSelectionKey(): Long? = itemId

您可以*使用任何其他技术来生成选择键,只要它可以生成唯一的值即可。

为了使RecyclerView Selection插件正常工作,每当用户触摸RecyclerView小部件时,您都必须将触摸的坐标转换为ItemDetails对象。

创建一个扩展ItemDetailsLookup类的新类,并向其添加一个可以接受RecyclerView小部件作为参数的构造函数。 请注意,由于该类是抽象类,因此Android Studio会自动为其抽象方法生成一个存根。

class MyLookup(private val rv: RecyclerView)
    : ItemDetailsLookup<String>() {
    override fun getItemDetails(event: MotionEvent)
                                : ItemDetails<String>? {

        // More code here
        
    }
}

如您在上面的代码中看到的, getItemDetails()方法接收一个MotionEvent对象。 通过将事件的X和Y坐标传递给findChildViewUnder()方法,可以确定与用户触摸的列表项关联的视图。 要将View对象转换为ItemDetails对象,您需要做的就是调用getItemDetails()方法。 这是如何做:

val view = rv.findChildViewUnder(event.x, event.y)
if(view != null) {
    return (rv.getChildViewHolder(view) as MyViewHolder)
            .getItemDetails()
}
return null

现在,您需要一个可以将您的列表绑定到RecyclerView小部件的适配器。 要创建一个类,请创建一个扩展RecyclerView.Adapter类的新类。 因为适配器需要访问列表和活动的上下文,所以新类必须具有一个可以接受这两个参数的构造函数。

class MyAdapter(private val listItems:List<Person>,
                private val context: Context)
    : RecyclerView.Adapter<MyViewHolder>() {

}

重要的是,您必须明确指出此适配器的每个项目都将具有类型为Long的唯一稳定标识符。 这样做的最佳位置是init块内。

init {
    setHasStableIds(true)
}

另外,为了能够使用项目的位置作为其唯一标识符,您必须重写getItemId()方法。

override fun getItemId(position: Int): Long {
    return position.toLong()
}

由于RecyclerView.Adapter类是抽象的,因此您现在必须重写三个以上的方法才能使适配器可用。

首先,重写getItemCount()方法以返回列表的大小。

override fun getItemCount(): Int = listItems.size

接下来,重写onCreateViewHolder()方法。 此方法必须返回您在本教程前面创建的视图持有器类的实例。 要创建这样的实例,您必须调用该类的构造函数,并将列表项的膨胀布局传递给它。 要使布局膨胀,请使用LayoutInflater类的LayoutInflater inflate()方法。 这是如何做:

override fun onCreateViewHolder(parent: ViewGroup,
                                viewType: Int): MyViewHolder = 
        MyViewHolder(
            LayoutInflater.from(context)
                .inflate(R.layout.list_item, parent, false)
        )

最后,重写onBindViewHolder()方法并适当地初始化视图持有者中存在的两个TextView小部件的text属性。

override fun onBindViewHolder(vh: MyViewHolder, position: Int) {
    vh.name.text = listItems[position].name
    vh.phone.text = listItems[position].phone
}

此时,您几乎拥有呈现列表所需的一切。 但是,您仍然必须指定如何定位列表项。 现在,让我们使用LinearLayoutManager实例将它们一个放在另一个下面。

为了获得最佳性能,我还建议您指出RecyclerView小部件的大小在运行时不会改变。

将以下代码添加到您的主要活动中:

my_rv.layoutManager = LinearLayoutManager(this)
my_rv.setHasFixedSize(true)

最后,将适配器的新实例分配给RecyclerView小部件的adapter属性。

val adapter = MyAdapter(myList, this)
my_rv.adapter = adapter

如果您现在运行应用程序,则可以看到列表。

如何将多个选择添加到Android RecyclerView

RecyclerView小部件仍然不允许您选择任何项目。 要启用多项目选择,您的活动中将需要一个SelectionTracker对象。

private var tracker: SelectionTracker<Long>? = null

您可以使用SelectionTracker.Builder类初始化跟踪器。 向其构造函数传递的是选择ID, RecyclerView小部件,**提供者,商品详细信息查找类和存储策略。

您可以随意使用任何字符串作为选择ID。 作为**提供者,可以使用StableIdKeyProvider类的实例。

RecyclerView选择库提供了多种存储策略,所有这些策略都可以确保在用户设备旋转或资源紧缩期间Android系统关闭您的应用程序时,不会取消选择选定的项目。 现在,由于选择键的类型为Long ,因此必须使用Long类型的StorageStrategy对象。

Builder准备就绪后,您可以调用其withSelectionPredicate()方法来指定要允许用户选择的项目数。 为了支持多项目选择,作为方法的参数,必须传递createSelectAnything()方法返回的SelectionPredicate对象。

因此,在活动的onCreate()方法内添加以下代码:

tracker = SelectionTracker.Builder<Long>(
                "selection-1",
                my_rv,
                StableIdKeyProvider(my_rv),
                MyLookup(my_rv),
                StorageStrategy.createLongStorage()
          ).withSelectionPredicate(
                SelectionPredicates.createSelectAnything()
          ).build()

为了充分利用存储策略,您必须始终尝试在onCreate()方法中还原跟踪器的状态。

if(savedInstanceState != null)
        tracker?.onRestoreInstanceState(savedInstanceState)

同样,您必须确保将跟踪器的状态保存在活动的onSaveInstanceState()方法中。

override fun onSaveInstanceState(outState: Bundle?) {
    super.onSaveInstanceState(outState)

    if(outState != null)
        tracker?.onSaveInstanceState(outState)
}

除非它与您的适配器关联,否则选择跟踪器不是很有用。 因此,通过调用setTracker()方法将其传递给适配器。

adapter.setTracker(tracker)

setTracker()方法尚不存在,因此在适配器类中添加以下代码:

private var tracker: SelectionTracker<Long>? = null

fun setTracker(tracker: SelectionTracker<Long>?) {
    this.tracker = tracker
}

如果您此时尝试运行应用程序,则可以选择列表中的项目。 通过长按列表项进入多项目选择模式时,大多数设备上都会出现短暂的振动。 但是,由于当前无法将所选项目与未选中项区分开,因此您将没有视觉反馈。 要解决此问题,您需要在适配器的onBindViewHolder()方法内进行一些更改。

突出显示所选项目的常规方法是更改​​其背景颜色。 因此,您现在必须更改项目的布局XML文件中存在的LinearLayout小部件的背景颜色。 要获取对它的引用,请获取对视图保持器中可用的TextView小部件之一的父级的引用。

onBindViewHolder()方法结尾之前添加以下代码:

val parent = vh.name.parent as LinearLayout

// More code here

接下来,可以调用SelectionTracker对象的isSelected()方法来确定是否SelectionTracker了一项。

以下代码显示了如何将所选项目的背景颜色更改为青色:

if(tracker!!.isSelected(position.toLong())) {
    parent.background = ColorDrawable(
            Color.parseColor("#80deea")
    )
} else {
    // Reset color to white if not selected
    parent.background = ColorDrawable(Color.WHITE)
}

如果您现在运行该应用程序,则应该能够看到您选择的项目。

如何将多个选择添加到Android RecyclerView

通常,您想向用户显示当前选择了多少个项目。 使用RecyclerView Selection库,这样做非常容易。

通过调用addObserver()方法,将SelectionObserver对象与选择跟踪器addObserver() 在观察者的onSelectionChanged()方法内部,您可以检测到所选项目数的变化。

tracker?.addObserver(
        object: SelectionTracker.SelectionObserver<Long>() {
            override fun onSelectionChanged() {
                val nItems:Int? = tracker?.selection?.size()
        
                // More code here
            }
})

如何显示所选项目的数量取决于您。 现在,我建议您直接在活动的操作栏中显示数字。 (可选)您还可以更改操作栏的背景颜色,以使用户知道列表中有活动的选择。 以下代码向您展示了如何:

if(nItems!=null && nItems > 0) {

    // Change title and color of action bar

    title = "$nItems items selected"
    supportActionBar?.setBackgroundDrawable(
            ColorDrawable(Color.parseColor("#ef6c00")))
} else {

    // Reset color and title to default values    

    title = "RVSelection"
    supportActionBar?.setBackgroundDrawable(
            ColorDrawable(getColor(R.color.colorPrimary)))
}

如果再次运行该应用程序,现在应该会看到标题更改,以反映您选择的列表项的数量。

如何将多个选择添加到Android RecyclerView

结论

在本教程中,您学习了如何使用RecyclerView Selection加载项库向RecyclerView小部件添加简单的项目选择支持。 您还学习了如何动态更改选定项目的外观,以便用户可以将其与未选定项目区分开。

要了解有关该库的更多信息,请参阅官方文档

翻译自: https://code.tutsplus.com/tutorials/how-to-add-selection-support-to-a-recyclerview--cms-32175