Android ListAdapter适配Transaction与Bank两种数据模型时类型参数报错问题咨询
First, let's clarify why your initial approach failed: ListAdapter is designed to work with a single data type as its first generic parameter (the definition is ListAdapter<T, VH extends RecyclerView.ViewHolder>), so passing three types like ListAdapter<Transaction, Bank, RecyclerView.ViewHolder> will always throw a "requires 2 type arguments" error.
To handle both Transaction and Bank models in the same adapter, we'll use a sealed class to wrap both types into a unified list item, then adjust the adapter to work with this wrapper. Here's a step-by-step implementation:
Step 1: Create a Sealed Class for Unified List Items
This class will act as a container for both your models, letting the adapter distinguish between them:
sealed class ListItem { data class TransactionItem(val transaction: Transaction) : ListItem() data class BankItem(val bank: Bank) : ListItem() }
Step 2: Update the DiffUtil Callback
We need a DiffUtil.ItemCallback that can compare our sealed class items:
class ListItemDiffCallback : DiffUtil.ItemCallback<ListItem>() { override fun areItemsTheSame(oldItem: ListItem, newItem: ListItem): Boolean { return when { oldItem is ListItem.TransactionItem && newItem is ListItem.TransactionItem -> oldItem.transaction.transId == newItem.transaction.transId oldItem is ListItem.BankItem && newItem is ListItem.BankItem -> oldItem.bank.id == newItem.bank.id // Replace with Bank's unique identifier else -> false } } override fun areContentsTheSame(oldItem: ListItem, newItem: ListItem): Boolean { return oldItem == newItem } }
Step 3: Rewrite the Adapter Class
Modify your TransactionHistory adapter to use ListItem as the data type, and add logic to handle both item types:
class TransactionHistory() : ListAdapter<ListItem, RecyclerView.ViewHolder>(ListItemDiffCallback()) { private val ITEM_VIEW_TYPE_TRANSACTION = 1 private val ITEM_VIEW_TYPE_BANK = 2 private val ITEM_VIEW_TYPE_EMPTY = 0 override fun getItemViewType(position: Int): Int { return if (itemCount == 0) { ITEM_VIEW_TYPE_EMPTY } else { when (getItem(position)) { is ListItem.TransactionItem -> ITEM_VIEW_TYPE_TRANSACTION is ListItem.BankItem -> ITEM_VIEW_TYPE_BANK } } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { ITEM_VIEW_TYPE_TRANSACTION -> TransactionViewHolder.from(parent) ITEM_VIEW_TYPE_BANK -> BankViewHolder.from(parent) ITEM_VIEW_TYPE_EMPTY -> EmptyViewHolder.from(parent) else -> throw ClassCastException("Unknown viewType $viewType") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val item = getItem(position) when (holder) { is TransactionViewHolder -> { val transactionItem = item as ListItem.TransactionItem holder.bind(transactionItem.transaction, clickListener) } is BankViewHolder -> { val bankItem = item as ListItem.BankItem holder.bind(bankItem.bank, clickListener) } is EmptyViewHolder -> holder.bind() } } lateinit var clickListener: AdapterListener2 fun setOnclickListener(listener: AdapterListener2) { clickListener = listener } // Existing Transaction ViewHolder (renamed for clarity) class TransactionViewHolder private constructor(val binding: ItemUserTransactionListBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(item: Transaction, adapterListener2: AdapterListener2) { binding.transaction = item binding.clickListener = adapterListener2 binding.executePendingBindings() binding.transactionStatus.text = when(item.type) { "payPayment" -> "برداخت قسط" "decrease" -> "برداشت" "increase" -> "واریز" else -> "" } binding.amount.text = item.decrease ?: item.increase } companion object { fun from(parent: ViewGroup): TransactionViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ItemUserTransactionListBinding.inflate(inflater, parent, false) return TransactionViewHolder(binding) } } } // New ViewHolder for Bank items class BankViewHolder private constructor(val binding: ItemBankInfoBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(item: Bank, adapterListener2: AdapterListener2) { binding.bankInfo = item binding.clickListener = adapterListener2 binding.executePendingBindings() // Add any Bank-specific binding logic here } companion object { fun from(parent: ViewGroup): BankViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ItemBankInfoBinding.inflate(inflater, parent, false) return BankViewHolder(binding) } } } // Empty ViewHolder remains unchanged class EmptyViewHolder private constructor(val binding: ItemUserTransactionListBinding) : RecyclerView.ViewHolder(binding.root) { fun bind() { binding.executePendingBindings() } companion object { fun from(parent: ViewGroup): EmptyViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ItemUserTransactionListBinding.inflate(inflater, parent, false) return EmptyViewHolder(binding) } } } }
Step 4: Combine and Submit Data
To use the adapter, merge your Transaction and Bank lists into a single list of ListItem (this is what the "join operation" suggestion refers to):
// Fetch your data sources val transactions: List<Transaction> = getTransactions() val banks: List<Bank> = getBanks() // Combine into a unified list val combinedList = transactions.map { ListItem.TransactionItem(it) } + banks.map { ListItem.BankItem(it) } // Submit to adapter transactionHistoryAdapter.submitList(combinedList)
Notes:
- If
TransactionandBankcan share the same layout, you can skip creating a separateBankViewHolderand modify the existingTransactionViewHolderto handle both item types, but separate ViewHolders keep your code cleaner. - Update
AdapterListener2if you need different click logic forBankitems (e.g., add a bank-specific click handler).
内容的提问来源于stack exchange,提问作者AmirHossein

