通用 RecyclerView 适配器

问题描述 投票:0回答:6

我想要通用的

RecyclerView
以便能够重用它。就我而言,我有 2 个模型:
CategoryImages
Category
。在尝试添加
constructor()
时,它会带来以下错误。我知道第二个是因为它理解主构造函数和辅助构造函数是相同的。

这样的事可以做吗?如果,那么怎么样?如果 - 谢谢。

这是

CategoryImage

class CategoryImage {
   @SerializedName("url")
   private var url: String? = null
       fun getUrl(): String? {
       return url
   }
}

这是

Category

class Category {
    @SerializedName("_id")
    var id: String? = null
    @SerializedName("name")
    var name: String? = null
    @SerializedName("__v")
    var v: Int? = null
    @SerializedName("thumbnail")
    var thumbnail: String? = null
}

这是

RecyclerViewAdapter
的构造函数的一部分:

class RecyclerViewAdapter(var arrayList: ArrayList<CategoryImage>?, var fragment: Int): RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
     constructor(arrayList: ArrayList<Category>, fragment: Int): this(arrayList, fragment)
}
android kotlin android-recyclerview recyclerview-layout
6个回答
2
投票

我想要通用的

RecyclerView
以便能够重复使用它。

这是一个很好的意图,那为什么你还没有制作你的适配器generic

我认为您可以采用 Arman Chatikyan 在这篇博文中概述的方法。应用一些 Kotlin 魔法后,您只需要以下几行代码即可设置您的

RecyclerView
:

recyclerView.setUp(users, R.layout.item_layout, {
    nameText.text = it.name
    surNameText.text = it.surname
})

如果您需要处理对

RecyclerView
项目的点击:

recyclerView.setUp(users, R.layout.item_layout, {
    nameText.text = it.name
    surNameText.text = it.surname
}, {
    toast("Clicked $name")
})

现在

RecyclerView
的适配器是通用的,您可以在
setup()
方法的第一个参数中传递任何模型的列表。


在本节中,我将从博客文章中复制粘贴源代码,以避免外部源弃用。

fun <ITEM> RecyclerView.setUp(items: List<ITEM>,
                              layoutResId: Int,
                              bindHolder: View.(ITEM) -> Unit,
                              itemClick: ITEM.() -> Unit = {},
                              manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context)): Kadapter<ITEM> {
  return Kadapter(items, layoutResId, {
    bindHolder(it)
  }, {
    itemClick()
  }).apply {
    layoutManager = manager
    adapter = this
  }
}

class Kadapter<ITEM>(items: List<ITEM>,
                     layoutResId: Int,
                     private val bindHolder: View.(ITEM) -> Unit)
  : AbstractAdapter<ITEM>(items, layoutResId) {

  private var itemClick: ITEM.() -> Unit = {}

  constructor(items: List<ITEM>,
              layoutResId: Int,
              bindHolder: View.(ITEM) -> Unit,
              itemClick: ITEM.() -> Unit = {}) : this(items, layoutResId, bindHolder) {
    this.itemClick = itemClick
  }

  override fun onBindViewHolder(holder: Holder, position: Int) {
    holder.itemView.bindHolder(itemList[position])
  }

  override fun onItemClick(itemView: View, position: Int) {
    itemList[position].itemClick()
  }
}

abstract class AbstractAdapter<ITEM> constructor(
    protected var itemList: List<ITEM>,
    private val layoutResId: Int)
  : RecyclerView.Adapter<AbstractAdapter.Holder>() {

  override fun getItemCount() = itemList.size

  override fun onCreateViewHolder(parent: ViewGroup,
                                  viewType: Int): Holder {
    val view = LayoutInflater.from(parent.context).inflate(layoutResId, parent, false)
    return Holder(view)
  }

  override fun onBindViewHolder(holder: Holder, position: Int) {
    val item = itemList[position]
    holder.itemView.bind(item)
  }

  protected abstract fun onItemClick(itemView: View, position: Int)

  protected open fun View.bind(item: ITEM) {
  }

  class Holder(itemView: View) : RecyclerView.ViewHolder(itemView)
}

1
投票

假设

CategoryImage
表示带有图像的
Category

你可以用继承来表达这种关系:

open class Category(
        val name: String
)
class CategoryImage(
        name: String,
        val image: String
) : Category(name)

class RecyclerViewAdapter(
        val arr: List<Category>,
        val fragment: Int
) {
    fun bind(i: Int) {
        val item = arr[i]
        val name: String = item.name
        val image: String? = (item as? CategoryImage)?.image
    }
}

另一个选项是它有一个通用的界面(这消除了丑陋的演员表):

interface CategoryLike {
    val name: String
    val image: String?
}

class Category(
        override val name: String
) : CategoryLike {
    override val image: String? = null
}

class CategoryImage(
        override val name: String,
        override val image: String
) : CategoryLike

class RecyclerViewAdapter(private var arr: List<CategoryLike>, var fragment: Int) {
    fun bind(i: Int) {
        val item = arr[i]
        val name: String = item.name
        val image: String? = item.image
    }
}

在这两种情况下,以下工作都有效(只是为了看看它是否可以编译):

fun testCreation() {
    val cats: List<Category> = listOf()
    val catImages: List<CategoryImage> = listOf()
    RecyclerViewAdapter(cats, 0)
    RecyclerViewAdapter(catImages, 0)
}

提示:不要使用

ArrayList
List
(
listOf(...)
) 或
MutableList
(
mutableListOf(...)
) 应该足以满足您的所有需求。

提示:尽可能多地使用

val
,这有助于防止错误。


0
投票

一种“糟糕”的做法是简单地让 1 个构造函数采用对象的 ArrayList 并在对象上执行

instanceof


0
投票

这两个方法具有相同的签名,因为类型参数不被视为不同的类型(对于 Java 虚拟机来说,两者都只是

ArrayList
)。您还需要了解类型擦除。


0
投票

检查此存储库https://github.com/shashank1800/RecyclerGenericAdapter

lateinit var adapter: RecyclerGenericAdapter<AdapterItemBinding, TestModel>
...

val clickListener = ArrayList<CallBackModel<AdapterItemBinding, TestModel>>()
clickListener.add(CallBackModel(R.id.show) { model, position, binding ->
    Toast.makeText(context, "Show button clicked at $position", Toast.LENGTH_SHORT)
        .show()
})

adapter = RecyclerGenericAdapter(
    R.layout.adapter_item, // layout for adapter
    BR.testModel,          // model variable name which is in xml
    clickListener          // adding click listeners is optional
)

binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = LinearLayoutManager(this)

adapter.submitList(viewModel.testModelList)

回收器适配器项目 R.layout.adapter_item XML。


<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="testModel"
            type="com.packagename.model.TestModel" />

    </data>
    ...

-1
投票

非常重要的注意事项:我对所有屏幕使用相同的布局。

    //********Adapter*********
    
    // include a template parameter T which allows Any datatype
    class MainAdapter<T : Any>(var data: List<T>) : RecyclerView.Adapter<MainViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
            val view = parent.inflateLayout()
            return MainViewHolder(view)
        }
    
        override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
            val item = data[position]
            holder.bind(item)
        }
    
        override fun getItemCount(): Int = data.size
    
    
        class MainViewHolder(private val binding: MainItemsListBinding) :
            RecyclerView.ViewHolder(binding.root) {
            // do the same for for bind function on Viewholder
            fun <T : Any> bind(item: T) {
                // Extension function see code below
                binding.appInfo.mySpannedString(item)
            }
        }
    }
    
        //Cast Item to type
    fun <T : Any> TextView.mySpannedString(item: T) {
        when (item.javaClass.simpleName) {
            "AaProgram" -> {
                item as AaProgram
                this.text = buildSpannedString {
                    appInfo(item.numero, item.principio)
                }
            }
            "AppContent" -> {
                item as AppContent
                this.text = buildSpannedString {
                    appInfo(item.title, item.coment, item.footnote)
                }
            }
            "AutoDiagnostic" -> {
                item as AppContent
                this.text = buildSpannedString {
                    appInfo(item.title, item.coment, item.footnote)
                }
            }
            "GroupDirectory" -> {}
            "ReflexionsBook" -> {}
            "County" -> {}
            "States" -> {}
            "Towns" -> {}
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.