|
|
@ -4,13 +4,11 @@ import android.content.Context |
|
|
|
import android.util.SparseArray |
|
|
|
import android.util.SparseArray |
|
|
|
import android.view.LayoutInflater |
|
|
|
import android.view.LayoutInflater |
|
|
|
import android.view.ViewGroup |
|
|
|
import android.view.ViewGroup |
|
|
|
import androidx.recyclerview.widget.AsyncListDiffer |
|
|
|
|
|
|
|
import androidx.recyclerview.widget.DiffUtil |
|
|
|
import androidx.recyclerview.widget.DiffUtil |
|
|
|
import androidx.recyclerview.widget.GridLayoutManager |
|
|
|
import androidx.recyclerview.widget.GridLayoutManager |
|
|
|
import androidx.recyclerview.widget.RecyclerView |
|
|
|
import androidx.recyclerview.widget.RecyclerView |
|
|
|
import androidx.viewbinding.ViewBinding |
|
|
|
import androidx.viewbinding.ViewBinding |
|
|
|
import java.util.* |
|
|
|
import java.util.* |
|
|
|
import kotlin.collections.ArrayList |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Created by Invincible on 2017/11/24. |
|
|
|
* Created by Invincible on 2017/11/24. |
|
|
@ -21,27 +19,12 @@ import kotlin.collections.ArrayList |
|
|
|
abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) : |
|
|
|
abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) : |
|
|
|
RecyclerView.Adapter<ItemViewHolder>() { |
|
|
|
RecyclerView.Adapter<ItemViewHolder>() { |
|
|
|
|
|
|
|
|
|
|
|
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM, VB>) : this(context) { |
|
|
|
|
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
context: Context, |
|
|
|
|
|
|
|
vararg delegates: Pair<Int, ItemViewDelegate<ITEM, VB>> |
|
|
|
|
|
|
|
) : this(context) { |
|
|
|
|
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
val inflater: LayoutInflater = LayoutInflater.from(context) |
|
|
|
val inflater: LayoutInflater = LayoutInflater.from(context) |
|
|
|
|
|
|
|
|
|
|
|
private val headerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
|
|
|
private val headerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
|
|
|
private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
|
|
|
private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
|
|
|
|
|
|
|
|
|
|
|
private val itemDelegates: HashMap<Int, ItemViewDelegate<ITEM, VB>> = hashMapOf() |
|
|
|
private val items: MutableList<ITEM> = mutableListOf() |
|
|
|
|
|
|
|
|
|
|
|
private val asyncListDiffer: AsyncListDiffer<ITEM> by lazy { |
|
|
|
|
|
|
|
AsyncListDiffer(this, diffItemCallback) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val lock = Object() |
|
|
|
private val lock = Object() |
|
|
|
|
|
|
|
|
|
|
@ -50,19 +33,6 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
|
|
|
|
|
|
|
|
var itemAnimation: ItemAnimation? = null |
|
|
|
var itemAnimation: ItemAnimation? = null |
|
|
|
|
|
|
|
|
|
|
|
open val diffItemCallback: DiffUtil.ItemCallback<ITEM> = |
|
|
|
|
|
|
|
object : DiffUtil.ItemCallback<ITEM>() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun areItemsTheSame(oldItem: ITEM, newItem: ITEM): Boolean { |
|
|
|
|
|
|
|
return oldItem == newItem |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun areContentsTheSame(oldItem: ITEM, newItem: ITEM): Boolean { |
|
|
|
|
|
|
|
return true |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { |
|
|
|
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { |
|
|
|
itemClickListener = listener |
|
|
|
itemClickListener = listener |
|
|
|
} |
|
|
|
} |
|
|
@ -75,28 +45,6 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
recyclerView.adapter = this |
|
|
|
recyclerView.adapter = this |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM, VB>> addItemViewDelegate( |
|
|
|
|
|
|
|
viewType: Int, |
|
|
|
|
|
|
|
delegate: DELEGATE |
|
|
|
|
|
|
|
) { |
|
|
|
|
|
|
|
itemDelegates[viewType] = delegate |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItemViewDelegate(delegate: ItemViewDelegate<ITEM, VB>) { |
|
|
|
|
|
|
|
itemDelegates[itemDelegates.size] = delegate |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItemViewDelegates(vararg delegates: ItemViewDelegate<ITEM, VB>) { |
|
|
|
|
|
|
|
delegates.forEach { |
|
|
|
|
|
|
|
addItemViewDelegate(it) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItemViewDelegates(vararg delegates: Pair<Int, ItemViewDelegate<ITEM, VB>>) = |
|
|
|
|
|
|
|
delegates.forEach { |
|
|
|
|
|
|
|
addItemViewDelegate(it.first, it.second) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { |
|
|
|
fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val index = headerItems.size() |
|
|
|
val index = headerItems.size() |
|
|
@ -133,65 +81,88 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
|
|
|
|
|
|
|
|
fun setItems(items: List<ITEM>?) { |
|
|
|
fun setItems(items: List<ITEM>?) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
asyncListDiffer.submitList(items) |
|
|
|
if (this.items.isNotEmpty()) { |
|
|
|
|
|
|
|
this.items.clear() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (items != null) { |
|
|
|
|
|
|
|
this.items.addAll(items) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
notifyDataSetChanged() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setItems(items: List<ITEM>?, diffResult: DiffUtil.DiffResult) { |
|
|
|
|
|
|
|
synchronized(lock) { |
|
|
|
|
|
|
|
if (this.items.isNotEmpty()) { |
|
|
|
|
|
|
|
this.items.clear() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (items != null) { |
|
|
|
|
|
|
|
this.items.addAll(items) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
diffResult.dispatchUpdatesTo(this) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun setItem(position: Int, item: ITEM) { |
|
|
|
fun setItem(position: Int, item: ITEM) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
val oldSize = getActualItemCount() |
|
|
|
list[position] = item |
|
|
|
if (position in 0 until oldSize) { |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
this.items[position] = item |
|
|
|
|
|
|
|
notifyItemChanged(position + getHeaderCount()) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun addItem(item: ITEM) { |
|
|
|
fun addItem(item: ITEM) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
val oldSize = getActualItemCount() |
|
|
|
list.add(item) |
|
|
|
if (this.items.add(item)) { |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
notifyItemInserted(oldSize + getHeaderCount()) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun addItems(position: Int, newItems: List<ITEM>) { |
|
|
|
fun addItems(position: Int, newItems: List<ITEM>) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
if (this.items.addAll(position, newItems)) { |
|
|
|
list.addAll(position, newItems) |
|
|
|
notifyItemRangeInserted(position + getHeaderCount(), newItems.size) |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun addItems(newItems: List<ITEM>) { |
|
|
|
fun addItems(newItems: List<ITEM>) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
val oldSize = getActualItemCount() |
|
|
|
list.addAll(newItems) |
|
|
|
if (this.items.addAll(newItems)) { |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
if (oldSize == 0 && getHeaderCount() == 0) { |
|
|
|
|
|
|
|
notifyDataSetChanged() |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
notifyItemRangeInserted(oldSize + getHeaderCount(), newItems.size) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun removeItem(position: Int) { |
|
|
|
fun removeItem(position: Int) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
if (this.items.removeAt(position) != null) { |
|
|
|
if (list.removeAt(position) != null) { |
|
|
|
notifyItemRemoved(position + getHeaderCount()) |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun removeItem(item: ITEM) { |
|
|
|
fun removeItem(item: ITEM) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
if (this.items.remove(item)) { |
|
|
|
if (list.remove(item)) { |
|
|
|
notifyItemRemoved(this.items.indexOf(item) + getHeaderCount()) |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun removeItems(items: List<ITEM>) { |
|
|
|
fun removeItems(items: List<ITEM>) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val list = ArrayList(asyncListDiffer.currentList) |
|
|
|
if (this.items.removeAll(items)) { |
|
|
|
if (list.removeAll(items)) { |
|
|
|
notifyDataSetChanged() |
|
|
|
asyncListDiffer.submitList(list) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -202,7 +173,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
if (oldPosition in 0 until size && newPosition in 0 until size) { |
|
|
|
if (oldPosition in 0 until size && newPosition in 0 until size) { |
|
|
|
val srcPosition = oldPosition + getHeaderCount() |
|
|
|
val srcPosition = oldPosition + getHeaderCount() |
|
|
|
val targetPosition = newPosition + getHeaderCount() |
|
|
|
val targetPosition = newPosition + getHeaderCount() |
|
|
|
Collections.swap(asyncListDiffer.currentList, srcPosition, targetPosition) |
|
|
|
Collections.swap(this.items, srcPosition, targetPosition) |
|
|
|
notifyItemChanged(srcPosition) |
|
|
|
notifyItemChanged(srcPosition) |
|
|
|
notifyItemChanged(targetPosition) |
|
|
|
notifyItemChanged(targetPosition) |
|
|
|
} |
|
|
|
} |
|
|
@ -211,9 +182,9 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
|
|
|
|
|
|
|
|
fun updateItem(item: ITEM) = |
|
|
|
fun updateItem(item: ITEM) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val index = asyncListDiffer.currentList.indexOf(item) |
|
|
|
val index = this.items.indexOf(item) |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) { |
|
|
|
asyncListDiffer.currentList[index] = item |
|
|
|
this.items[index] = item |
|
|
|
notifyItemChanged(index) |
|
|
|
notifyItemChanged(index) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -240,17 +211,18 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
|
|
|
|
|
|
|
|
fun clearItems() = |
|
|
|
fun clearItems() = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
asyncListDiffer.submitList(arrayListOf()) |
|
|
|
this.items.clear() |
|
|
|
|
|
|
|
notifyDataSetChanged() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun isEmpty() = asyncListDiffer.currentList.isEmpty() |
|
|
|
fun isEmpty() = items.isEmpty() |
|
|
|
|
|
|
|
|
|
|
|
fun isNotEmpty() = asyncListDiffer.currentList.isNotEmpty() |
|
|
|
fun isNotEmpty() = items.isNotEmpty() |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 除去header和footer |
|
|
|
* 除去header和footer |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
fun getActualItemCount() = asyncListDiffer.currentList.size |
|
|
|
fun getActualItemCount() = items.size |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getHeaderCount() = headerItems.size() |
|
|
|
fun getHeaderCount() = headerItems.size() |
|
|
@ -258,12 +230,11 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
|
|
|
|
|
|
|
|
fun getFooterCount() = footerItems.size() |
|
|
|
fun getFooterCount() = footerItems.size() |
|
|
|
|
|
|
|
|
|
|
|
fun getItem(position: Int): ITEM? = asyncListDiffer.currentList.getOrNull(position) |
|
|
|
fun getItem(position: Int): ITEM? = items.getOrNull(position) |
|
|
|
|
|
|
|
|
|
|
|
fun getItemByLayoutPosition(position: Int) = |
|
|
|
fun getItemByLayoutPosition(position: Int) = items.getOrNull(position - getHeaderCount()) |
|
|
|
asyncListDiffer.currentList.getOrNull(position - getHeaderCount()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getItems(): List<ITEM> = asyncListDiffer.currentList |
|
|
|
fun getItems(): List<ITEM> = items |
|
|
|
|
|
|
|
|
|
|
|
protected open fun getItemViewType(item: ITEM, position: Int) = 0 |
|
|
|
protected open fun getItemViewType(item: ITEM, position: Int) = 0 |
|
|
|
|
|
|
|
|
|
|
@ -295,8 +266,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
val holder = ItemViewHolder(getViewBinding(parent)) |
|
|
|
val holder = ItemViewHolder(getViewBinding(parent)) |
|
|
|
|
|
|
|
|
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
itemDelegates.getValue(viewType) |
|
|
|
registerListener(holder, (holder.binding as VB)) |
|
|
|
.registerListener(holder, (holder.binding as VB)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (itemClickListener != null) { |
|
|
|
if (itemClickListener != null) { |
|
|
|
holder.itemView.setOnClickListener { |
|
|
|
holder.itemView.setOnClickListener { |
|
|
@ -330,8 +300,7 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
) { |
|
|
|
) { |
|
|
|
if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { |
|
|
|
if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { |
|
|
|
getItem(holder.layoutPosition - getHeaderCount())?.let { |
|
|
|
getItem(holder.layoutPosition - getHeaderCount())?.let { |
|
|
|
itemDelegates.getValue(getItemViewType(holder.layoutPosition)) |
|
|
|
convert(holder, (holder.binding as VB), it, payloads) |
|
|
|
.convert(holder, (holder.binding as VB), it, payloads) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -381,6 +350,22 @@ abstract class CommonRecyclerAdapter<ITEM, VB : ViewBinding>(protected val conte |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 如果使用了事件回调,回调里不要直接使用item,会出现不更新的问题, |
|
|
|
|
|
|
|
* 使用getItem(holder.layoutPosition)来获取item |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
abstract fun convert( |
|
|
|
|
|
|
|
holder: ItemViewHolder, |
|
|
|
|
|
|
|
binding: VB, |
|
|
|
|
|
|
|
item: ITEM, |
|
|
|
|
|
|
|
payloads: MutableList<Any> |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 注册事件 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
abstract fun registerListener(holder: ItemViewHolder, binding: VB) |
|
|
|
|
|
|
|
|
|
|
|
companion object { |
|
|
|
companion object { |
|
|
|
private const val TYPE_HEADER_VIEW = Int.MIN_VALUE |
|
|
|
private const val TYPE_HEADER_VIEW = Int.MIN_VALUE |
|
|
|
private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 |
|
|
|
private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 |
|
|
|