|
|
|
@ -15,67 +15,67 @@ import java.util.* |
|
|
|
|
* |
|
|
|
|
* 通用的adapter 可添加header,footer,以及不同类型item |
|
|
|
|
*/ |
|
|
|
|
abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
|
RecyclerView.Adapter<ItemViewHolder>() { |
|
|
|
|
|
|
|
|
|
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM>): this(context) { |
|
|
|
|
|
|
|
|
|
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM>) : this(context) { |
|
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
context: Context, |
|
|
|
|
vararg delegates: Pair<Int, ItemViewDelegate<ITEM>> |
|
|
|
|
): this(context) { |
|
|
|
|
) : this(context) { |
|
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val inflater: LayoutInflater = LayoutInflater.from(context) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var headerItems: SparseArray<View>? = null |
|
|
|
|
private var footerItems: SparseArray<View>? = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val itemDelegates: HashMap<Int, ItemViewDelegate<ITEM>> = hashMapOf() |
|
|
|
|
private val items: MutableList<ITEM> = mutableListOf() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private val lock = Object() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null |
|
|
|
|
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 这个用Kotlin的setter就行了, 不需要手动开一个函数进行设置 |
|
|
|
|
var itemAnimation: ItemAnimation? = null |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { |
|
|
|
|
itemClickListener = listener |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setOnItemLongClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Boolean) { |
|
|
|
|
itemLongClickListener = listener |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun bindToRecyclerView(recyclerView: RecyclerView) { |
|
|
|
|
recyclerView.adapter = this |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegate(viewType: Int, delegate: DELEGATE) { |
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegate(viewType: Int, delegate: DELEGATE) { |
|
|
|
|
itemDelegates[viewType] = delegate |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegate(delegate: DELEGATE) { |
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegate(delegate: DELEGATE) { |
|
|
|
|
itemDelegates[itemDelegates.size] = delegate |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegates(vararg delegates: DELEGATE) { |
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegates(vararg delegates: DELEGATE) { |
|
|
|
|
delegates.forEach { |
|
|
|
|
addItemViewDelegate(it) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItemViewDelegates(vararg delegates: Pair<Int, ItemViewDelegate<ITEM>>) = |
|
|
|
|
delegates.forEach { |
|
|
|
|
addItemViewDelegate(it.first, it.second) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addHeaderView(header: View) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (headerItems == null) { |
|
|
|
@ -88,7 +88,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addFooterView(footer: View) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (footerItems == null) { |
|
|
|
@ -100,8 +100,8 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
notifyItemInserted(index) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeHeaderView(header: View) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
headerItems?.let { |
|
|
|
@ -112,7 +112,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeFooterView(footer: View) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
footerItems?.let { |
|
|
|
@ -123,7 +123,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setItems(items: List<ITEM>?) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.isNotEmpty()) { |
|
|
|
@ -135,7 +135,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
notifyDataSetChanged() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setItems(items: List<ITEM>?, diffResult: DiffUtil.DiffResult) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.isNotEmpty()) { |
|
|
|
@ -147,7 +147,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
diffResult.dispatchUpdatesTo(this) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setItem(position: Int, item: ITEM) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val oldSize = getActualItemCount() |
|
|
|
@ -157,7 +157,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItem(item: ITEM) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val oldSize = getActualItemCount() |
|
|
|
@ -166,7 +166,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItems(position: Int, newItems: List<ITEM>) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.addAll(position, newItems)) { |
|
|
|
@ -174,7 +174,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addItems(newItems: List<ITEM>) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val oldSize = getActualItemCount() |
|
|
|
@ -187,7 +187,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeItem(position: Int) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.removeAt(position) != null) { |
|
|
|
@ -195,7 +195,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeItem(item: ITEM) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.remove(item)) { |
|
|
|
@ -203,7 +203,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeItems(items: List<ITEM>) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
if (this.items.removeAll(items)) { |
|
|
|
@ -211,7 +211,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun swapItem(oldPosition: Int, newPosition: Int) { |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val size = getActualItemCount() |
|
|
|
@ -224,7 +224,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun updateItem(item: ITEM) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val index = this.items.indexOf(item) |
|
|
|
@ -233,7 +233,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
notifyItemChanged(index) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun updateItem(position: Int, payload: Any) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val size = getActualItemCount() |
|
|
|
@ -241,7 +241,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
notifyItemChanged(position + getHeaderCount(), payload) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
val size = getActualItemCount() |
|
|
|
@ -253,43 +253,43 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun clearItems() = |
|
|
|
|
synchronized(lock) { |
|
|
|
|
this.items.clear() |
|
|
|
|
notifyDataSetChanged() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun isEmpty() = items.isEmpty() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun isNotEmpty() = items.isNotEmpty() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 除去header和footer |
|
|
|
|
*/ |
|
|
|
|
fun getActualItemCount() = items.size |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getHeaderCount() = headerItems?.size() ?: 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getFooterCount() = footerItems?.size() ?: 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getItem(position: Int): ITEM? = items.getOrNull(position) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getItemByLayoutPosition(position: Int) = items.getOrNull(position - getHeaderCount()) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getItems(): List<ITEM> = items |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected open fun getItemViewType(item: ITEM, position: Int) = 0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* grid 模式下使用 |
|
|
|
|
*/ |
|
|
|
|
protected open fun getSpanSize(item: ITEM, viewType: Int, position: Int) = 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun getItemCount() = getActualItemCount() + getHeaderCount() + getFooterCount() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun getItemViewType(position: Int) = when { |
|
|
|
|
isHeader(position) -> TYPE_HEADER_VIEW + position |
|
|
|
|
isFooter(position) -> TYPE_FOOTER_VIEW + position - getActualItemCount() - getHeaderCount() |
|
|
|
@ -297,16 +297,16 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
getItemViewType(it, getActualPosition(position)) |
|
|
|
|
} ?: 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when { |
|
|
|
|
viewType < TYPE_HEADER_VIEW + getHeaderCount() -> { |
|
|
|
|
ItemViewHolder(headerItems!!.get(viewType)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
viewType >= TYPE_FOOTER_VIEW -> { |
|
|
|
|
ItemViewHolder(footerItems!!.get(viewType)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else -> { |
|
|
|
|
val holder = ItemViewHolder( |
|
|
|
|
inflater.inflate( |
|
|
|
@ -315,10 +315,10 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
false |
|
|
|
|
) |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
itemDelegates.getValue(viewType) |
|
|
|
|
.registerListener(holder) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (itemClickListener != null) { |
|
|
|
|
holder.itemView.setOnClickListener { |
|
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
@ -326,7 +326,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (itemLongClickListener != null) { |
|
|
|
|
holder.itemView.setOnLongClickListener { |
|
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
@ -334,13 +334,13 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} ?: true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
holder |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun onBindViewHolder( |
|
|
|
|
holder: ItemViewHolder, |
|
|
|
|
position: Int, |
|
|
|
@ -353,19 +353,19 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onViewAttachedToWindow(holder: ItemViewHolder) { |
|
|
|
|
super.onViewAttachedToWindow(holder) |
|
|
|
|
if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { |
|
|
|
|
addAnimation(holder) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { |
|
|
|
|
super.onAttachedToRecyclerView(recyclerView) |
|
|
|
|
val manager = recyclerView.layoutManager |
|
|
|
|
if (manager is GridLayoutManager) { |
|
|
|
|
manager.spanSizeLookup = object: GridLayoutManager.SpanSizeLookup() { |
|
|
|
|
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { |
|
|
|
|
override fun getSpanSize(position: Int): Int { |
|
|
|
|
return getItem(position)?.let { |
|
|
|
|
if (isHeader(position) || isFooter(position)) manager.spanCount else getSpanSize( |
|
|
|
@ -376,13 +376,13 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun isHeader(position: Int) = position < getHeaderCount() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun isFooter(position: Int) = position >= getActualItemCount() + getHeaderCount() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun getActualPosition(position: Int) = position - getHeaderCount() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun addAnimation(holder: ItemViewHolder) { |
|
|
|
|
itemAnimation?.let { |
|
|
|
|
if (it.itemAnimEnabled) { |
|
|
|
@ -393,7 +393,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { |
|
|
|
|
item.itemAnimation?.let { |
|
|
|
|
for (anim in it.getAnimators(holder.itemView)) { |
|
|
|
@ -402,12 +402,12 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
companion object { |
|
|
|
|
private const val TYPE_HEADER_VIEW = Int.MIN_VALUE |
|
|
|
|
private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|