Ich habe eine bessere Lösung, die es ermöglicht, mehrere Ansichtstypen auf eine deklarative und typsichere Weise zu erstellen. Es ist in Kotlin geschrieben, was übrigens wirklich schön ist.
Einfache View Holder für alle erforderlichen Ansichtstypen
class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
val label: TextView = itemView.findViewById(R.id.label) as TextView
}
Es gibt eine Abstraktion von Adapter-Datenpunkten. Beachten Sie, dass ein Ansichtstyp durch einen Hashcode der jeweiligen View Holder-Klasse (KClass in Kotlin) repräsentiert wird
trait AdapterItem {
val viewType: Int
fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
}
abstract class AdapterItemBase(val viewHolderClass: KClass) : AdapterItem {
override val viewType: Int = viewHolderClass.hashCode()
abstract fun bindViewHolder(viewHolder: T)
override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
bindViewHolder(viewHolder as T)
}
}
Nur bindViewHolder
muss in konkreten Adapter-Elementklassen überschrieben werden (typsichere Art und Weise).
class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase(ViewHolderMedium::class) {
override fun bindViewHolder(viewHolder: ViewHolderMedium) {
viewHolder.icon.setImageDrawable(icon)
viewHolder.label.setText(label)
viewHolder.itemView.setOnClickListener { onClick() }
}
}
Die Liste solcher AdapterItemMedium
-Objekte ist eine Datenquelle für den Adapter, der tatsächlich List
akzeptiert. Siehe unten.
Der wichtige Teil dieser Lösung ist eine View Holder-Fabrik, die frische Instanzen eines bestimmten ViewHolders bereitstellt:
class ViewHolderProvider {
private val viewHolderFactories = hashMapOf>()
fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
return viewHolderFactory(view)
}
fun registerViewHolderFactory(key: KClass, layoutId: Int, viewHolderFactory: (View) -> T) {
viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
}
}
Und die einfache Adapterklasse sieht so aus:
public class MultitypeAdapter(val items: List) : RecyclerView.Adapter() {
val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2
init {
viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
ViewHolderMedium(itemView)
})
}
override fun getItemViewType(position: Int): Int {
return items[position].viewType
}
override fun getItemCount(): Int {
return items.size()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
items[position].bindViewHolder(viewHolder)
}
}
Es gibt nur drei Schritte, um einen neuen Ansichtstyp zu erstellen:
- Erstellen Sie eine View Holder-Klasse
- Erstellen Sie eine Adapter-Item-Klasse (die von AdapterItemBase erbt)
- Registrieren Sie die View Holder-Klasse in
ViewHolderProvider
Hier ist ein Beispiel dieses Konzepts: android-drawer-template.
Es geht sogar noch weiter - ein Ansichtstyp, der als Spinner-Komponente fungiert, mit auswählbaren Adapter-Elementen.