Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 7 additions & 93 deletions library/src/main/kotlin/com/drakeet/multitype/MultiTypeAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package com.drakeet.multitype

import android.util.Log
import android.view.ViewGroup
import androidx.annotation.CheckResult
import androidx.recyclerview.widget.RecyclerView
Expand All @@ -39,25 +38,12 @@ open class MultiTypeAdapter @JvmOverloads constructor(
*/
open var items: List<Any> = emptyList(),
open val initialCapacity: Int = 0,
open var types: Types = MutableTypes(initialCapacity),
) : RecyclerView.Adapter<ViewHolder>() {
override var types: Types = MutableTypes(initialCapacity),
) : RecyclerView.Adapter<ViewHolder>(), MultiTypeDelegate {

/**
* Registers a type class and its item view delegate. If you have registered the class,
* it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter
* again.
*
* @param clazz the class of a item
* @param delegate the item view delegate
* @param T the item data type
* */
fun <T> register(clazz: Class<T>, delegate: ItemViewDelegate<T, *>) {
unregisterAllTypesIfNeeded(clazz)
register(Type(clazz, delegate, DefaultLinker()))
override fun <T> register(type: Type<T>) {
super.register(type)
type.delegate._adapter = this
}

inline fun <reified T : Any> register(delegate: ItemViewDelegate<T, *>) {
Expand Down Expand Up @@ -86,63 +72,17 @@ open class MultiTypeAdapter @JvmOverloads constructor(
register(clazz, binder as ItemViewDelegate<T, *>)
}

internal fun <T> register(type: Type<T>) {
types.register(type)
type.delegate._adapter = this
}

/**
* Registers a type class to multiple item view delegates. If you have registered the
* class, it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter again.
*
* @param clazz the class of a item
* @param <T> the item data type
* @return [OneToManyFlow] for setting the delegates
* @see [register]
*/
@CheckResult
fun <T> register(clazz: Class<T>): OneToManyFlow<T> {
unregisterAllTypesIfNeeded(clazz)
return OneToManyBuilder(this, clazz)
}

@CheckResult
fun <T : Any> register(clazz: KClass<T>): OneToManyFlow<T> {
return register(clazz.java)
}

/**
* Registers all of the contents in the specified [Types]. If you have registered a
* class, it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter
* again.
*
* @param types a [Types] containing contents to be added to this adapter inner [Types]
* @see [register]
* @see [register]
*/
fun registerAll(types: Types) {
val size = types.size
for (i in 0 until size) {
val type = types.getType<Any>(i)
unregisterAllTypesIfNeeded(type.clazz)
register(type)
}
}

override fun getItemViewType(position: Int): Int {
return indexInTypesOf(position, items[position])
}

override fun onCreateViewHolder(parent: ViewGroup, indexViewType: Int): ViewHolder {
return types.getType<Any>(indexViewType).delegate.onCreateViewHolder(parent.context, parent)
return getOutDelegate(indexViewType).onCreateViewHolder(parent.context, parent)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
Expand All @@ -166,9 +106,8 @@ open class MultiTypeAdapter @JvmOverloads constructor(
* @since v3.2.0
*/
override fun getItemId(position: Int): Long {
val item = items[position]
val itemViewType = getItemViewType(position)
return types.getType<Any>(itemViewType).delegate.getItemId(item)
return getOutDelegate(itemViewType).getItemId(position)
}

/**
Expand Down Expand Up @@ -224,29 +163,4 @@ open class MultiTypeAdapter @JvmOverloads constructor(
override fun onViewDetachedFromWindow(holder: ViewHolder) {
getOutDelegateByViewHolder(holder).onViewDetachedFromWindow(holder)
}

private fun getOutDelegateByViewHolder(holder: ViewHolder): ItemViewDelegate<Any, ViewHolder> {
@Suppress("UNCHECKED_CAST")
return types.getType<Any>(holder.itemViewType).delegate as ItemViewDelegate<Any, ViewHolder>
}

@Throws(DelegateNotFoundException::class)
internal fun indexInTypesOf(position: Int, item: Any): Int {
val index = types.firstIndexOf(item.javaClass)
if (index != -1) {
val linker = types.getType<Any>(index).linker
return index + linker.index(position, item)
}
throw DelegateNotFoundException(item.javaClass)
}

private fun unregisterAllTypesIfNeeded(clazz: Class<*>) {
if (types.unregister(clazz)) {
Log.w(TAG, "The type ${clazz.simpleName} you originally registered is now overwritten.")
}
}

companion object {
private const val TAG = "MultiTypeAdapter"
}
}
101 changes: 101 additions & 0 deletions library/src/main/kotlin/com/drakeet/multitype/MultiTypeDelegate.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package com.drakeet.multitype

import android.util.Log
import androidx.annotation.CheckResult
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder

interface MultiTypeDelegate {

val types: Types

fun <T> register(type: Type<T>) {
types.register(type)
}

/**
* Registers a type class and its item view delegate. If you have registered the class,
* it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter
* again.
*
* @param clazz the class of a item
* @param delegate the item view delegate
* @param T the item data type
* */
fun <T> register(clazz: Class<T>, delegate: ItemViewDelegate<T, *>) {
unregisterAllTypesIfNeeded(clazz)
register(Type(clazz, delegate, DefaultLinker()))
}

/**
* Registers a type class to multiple item view delegates. If you have registered the
* class, it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter again.
*
* @param clazz the class of a item
* @param <T> the item data type
* @return [OneToManyFlow] for setting the delegates
* @see [register]
*/
@CheckResult
fun <T> register(clazz: Class<T>): OneToManyFlow<T> {
unregisterAllTypesIfNeeded(clazz)
return OneToManyBuilder(this, clazz)
}

/**
* Registers all of the contents in the specified [Types]. If you have registered a
* class, it will override the original delegate(s). Note that the method is non-thread-safe
* so that you should not use it in concurrent operation.
*
* Note that the method should not be called after
* [RecyclerView.setAdapter], or you have to call the setAdapter
* again.
*
* @param types a [Types] containing contents to be added to this adapter inner [Types]
* @see [register]
* @see [register]
*/
fun registerAll(types: Types) {
val size = types.size
for (i in 0 until size) {
val type = types.getType<Any>(i)
unregisterAllTypesIfNeeded(type.clazz)
register(type)
}
}

@Throws(DelegateNotFoundException::class)
fun indexInTypesOf(position: Int, item: Any): Int {
val index = types.firstIndexOf(item.javaClass)
if (index != -1) {
val linker = types.getType<Any>(index).linker
return index + linker.index(position, item)
}
throw DelegateNotFoundException(item.javaClass)
}

fun getOutDelegate(itemViewType: Int): ItemViewDelegate<Any, *> {
return types.getType<Any>(itemViewType).delegate
}

fun getOutDelegateByViewHolder(holder: ViewHolder): ItemViewDelegate<Any, ViewHolder> {
@Suppress("UNCHECKED_CAST")
return getOutDelegate(holder.itemViewType) as ItemViewDelegate<Any, ViewHolder>
}
}

private fun MultiTypeDelegate.unregisterAllTypesIfNeeded(clazz: Class<*>) {
if (types.unregister(clazz)) {
Log.w(TAG, "The type ${clazz.simpleName} you originally registered is now overwritten.")
}
}

private const val TAG = "MultiTypeAdapter"
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.annotation.CheckResult
* @author Drakeet Xu
*/
internal class OneToManyBuilder<T>(
private val adapter: MultiTypeAdapter,
private val adapter: MultiTypeDelegate,
private val clazz: Class<T>
) : OneToManyFlow<T>, OneToManyEndpoint<T> {

Expand Down