安卓开发:RecyclerView适配不同属性数据,单Adapter还是多Adapter?
单个Adapter完全能适配不同分类的数据!
绝对可以用单个RecyclerView Adapter搞定所有分类的展示需求,这是Android开发中处理多类型列表的经典场景,完全没必要为每个分类单独编写Adapter——既冗余又增加后续维护成本。下面给你两种可行的实现思路,你可以根据自己的代码结构选择:
思路1:用密封类定义类型安全的数据模型(推荐)
这种方式类型更安全,代码可读性和可维护性更高,适合长期迭代的项目。
首先,创建一个密封类DataItem,然后为每个分类定义对应的子类,把该分类的字段明确写出来:
sealed class DataItem { // 姓名分类的数据类 data class NameItem(val forename: String, val surname: String) : DataItem() // 宝藏分类的数据类 data class TreasureItem(val style: String, val item: String, val value: String) : DataItem() // 商店分类的数据类 data class ShopItem(val name: String, val descriptor: String, val type: String, val quality: String) : DataItem() }
接下来编写通用Adapter,通过重写getItemViewType来区分不同分类的布局,然后在onCreateViewHolder和onBindViewHolder中分别处理:
class UniversalDataAdapter(private val dataList: List<DataItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val VIEW_TYPE_NAME = 0 private const val VIEW_TYPE_TREASURE = 1 private const val VIEW_TYPE_SHOP = 2 } override fun getItemViewType(position: Int): Int { return when (dataList[position]) { is DataItem.NameItem -> VIEW_TYPE_NAME is DataItem.TreasureItem -> VIEW_TYPE_TREASURE is DataItem.ShopItem -> VIEW_TYPE_SHOP } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { VIEW_TYPE_NAME -> { val view = inflater.inflate(R.layout.item_name, parent, false) NameViewHolder(view) } VIEW_TYPE_TREASURE -> { val view = inflater.inflate(R.layout.item_treasure, parent, false) TreasureViewHolder(view) } VIEW_TYPE_SHOP -> { val view = inflater.inflate(R.layout.item_shop, parent, false) ShopViewHolder(view) } else -> throw IllegalArgumentException("未知的布局类型") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = dataList[position]) { is DataItem.NameItem -> (holder as NameViewHolder).bind(item) is DataItem.TreasureItem -> (holder as TreasureViewHolder).bind(item) is DataItem.ShopItem -> (holder as ShopViewHolder).bind(item) } } override fun getItemCount() = dataList.size // 姓名分类的ViewHolder class NameViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val tvForename = itemView.findViewById<TextView>(R.id.tv_forename) private val tvSurname = itemView.findViewById<TextView>(R.id.tv_surname) fun bind(item: DataItem.NameItem) { tvForename.text = item.forename tvSurname.text = item.surname } } // 宝藏分类的ViewHolder class TreasureViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val tvStyle = itemView.findViewById<TextView>(R.id.tv_style) private val tvItem = itemView.findViewById<TextView>(R.id.tv_item) private val tvValue = itemView.findViewById<TextView>(R.id.tv_value) fun bind(item: DataItem.TreasureItem) { tvStyle.text = item.style tvItem.text = item.item tvValue.text = item.value } } // 商店分类的ViewHolder(实现类似) class ShopViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { // 初始化控件... fun bind(item: DataItem.ShopItem) { // 绑定数据... } } }
思路2:适配你现有的HashMap结构
如果你想直接复用当前的HashMap数据结构,也可以通过分类标识来区分布局:
首先定义一个通用数据类,包含分类名称和对应的HashMap:
data class GenericDataItem(val category: String, val dataMap: HashMap<String, String>)
然后在Adapter中根据category字段判断布局类型,绑定数据时直接从HashMap中取对应键的值:
class GenericDataAdapter(private val dataList: List<GenericDataItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { companion object { private const val VIEW_TYPE_NAME = 0 private const val VIEW_TYPE_TREASURE = 1 private const val VIEW_TYPE_SHOP = 2 } override fun getItemViewType(position: Int): Int { return when (dataList[position].category) { "姓名" -> VIEW_TYPE_NAME "宝藏" -> VIEW_TYPE_TREASURE "商店" -> VIEW_TYPE_SHOP else -> -1 } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { // 和思路1类似,根据viewType加载对应布局 // ... } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val item = dataList[position] when (holder.itemViewType) { VIEW_TYPE_NAME -> { val nameHolder = holder as NameViewHolder nameHolder.tvForename.text = item.dataMap["Forename"] nameHolder.tvSurname.text = item.dataMap["Surname"] } VIEW_TYPE_TREASURE -> { // 绑定宝藏分类的数据... } // 其他分类同理 } } // ViewHolder实现和思路1类似... }
总结
两种思路都能实现单个Adapter适配所有分类,推荐思路1,因为密封类的方式类型更安全,后续新增分类只需要新增对应的DataItem子类、布局和ViewHolder,不需要修改Adapter的核心逻辑,扩展性更好。
内容的提问来源于stack exchange,提问作者Black Spike
相关产品推荐
相关产品推荐

