|
|
@ -10,23 +10,22 @@ import androidx.recyclerview.widget.GridLayoutManager |
|
|
|
import androidx.recyclerview.widget.RecyclerView |
|
|
|
import androidx.recyclerview.widget.RecyclerView |
|
|
|
import java.util.* |
|
|
|
import java.util.* |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Created by Invincible on 2017/11/24. |
|
|
|
* Created by Invincible on 2017/11/24. |
|
|
|
* |
|
|
|
* |
|
|
|
* 通用的adapter 可添加header,footer,以及不同类型item |
|
|
|
* 通用的adapter 可添加header,footer,以及不同类型item |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context): |
|
|
|
RecyclerView.Adapter<ItemViewHolder>() { |
|
|
|
RecyclerView.Adapter<ItemViewHolder>() { |
|
|
|
|
|
|
|
|
|
|
|
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM>) : this(context) { |
|
|
|
constructor(context: Context, vararg delegates: ItemViewDelegate<ITEM>): this(context) { |
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
constructor( |
|
|
|
context: Context, |
|
|
|
context: Context, |
|
|
|
vararg delegates: Pair<Int, ItemViewDelegate<ITEM>> |
|
|
|
vararg delegates: Pair<Int, ItemViewDelegate<ITEM>> |
|
|
|
) : this(context) { |
|
|
|
): this(context) { |
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
addItemViewDelegates(*delegates) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -43,7 +42,8 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null |
|
|
|
private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null |
|
|
|
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null |
|
|
|
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null |
|
|
|
|
|
|
|
|
|
|
|
private var itemAnimation: ItemAnimation? = null |
|
|
|
// 这个用Kotlin的setter就行了, 不需要手动开一个函数进行设置 |
|
|
|
|
|
|
|
var itemAnimation: ItemAnimation? = null |
|
|
|
|
|
|
|
|
|
|
|
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { |
|
|
|
fun setOnItemClickListener(listener: (holder: ItemViewHolder, item: ITEM) -> Unit) { |
|
|
|
itemClickListener = listener |
|
|
|
itemClickListener = listener |
|
|
@ -57,25 +57,24 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
recyclerView.adapter = this |
|
|
|
recyclerView.adapter = this |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegate(viewType: Int, delegate: DELEGATE) { |
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegate(viewType: Int, delegate: DELEGATE) { |
|
|
|
itemDelegates[viewType] = delegate |
|
|
|
itemDelegates[viewType] = delegate |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegate(delegate: DELEGATE) { |
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegate(delegate: DELEGATE) { |
|
|
|
itemDelegates[itemDelegates.size] = delegate |
|
|
|
itemDelegates[itemDelegates.size] = delegate |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun <DELEGATE : ItemViewDelegate<ITEM>> addItemViewDelegates(vararg delegates: DELEGATE) { |
|
|
|
fun <DELEGATE: ItemViewDelegate<ITEM>> addItemViewDelegates(vararg delegates: DELEGATE) { |
|
|
|
delegates.forEach { |
|
|
|
delegates.forEach { |
|
|
|
addItemViewDelegate(it) |
|
|
|
addItemViewDelegate(it) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun addItemViewDelegates(vararg delegates: Pair<Int, ItemViewDelegate<ITEM>>) { |
|
|
|
fun addItemViewDelegates(vararg delegates: Pair<Int, ItemViewDelegate<ITEM>>) = |
|
|
|
delegates.forEach { |
|
|
|
delegates.forEach { |
|
|
|
addItemViewDelegate(it.first, it.second) |
|
|
|
addItemViewDelegate(it.first, it.second) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun addHeaderView(header: View) { |
|
|
|
fun addHeaderView(header: View) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
@ -90,7 +89,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun addFooterView(footer: View) { |
|
|
|
fun addFooterView(footer: View) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
if (footerItems == null) { |
|
|
|
if (footerItems == null) { |
|
|
|
footerItems = SparseArray() |
|
|
|
footerItems = SparseArray() |
|
|
@ -101,9 +100,9 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
notifyItemInserted(index) |
|
|
|
notifyItemInserted(index) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeHeaderView(header: View) { |
|
|
|
|
|
|
|
|
|
|
|
fun removeHeaderView(header: View) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
headerItems?.let { |
|
|
|
headerItems?.let { |
|
|
|
val index = it.indexOfValue(header) |
|
|
|
val index = it.indexOfValue(header) |
|
|
@ -113,9 +112,8 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun removeFooterView(footer: View) { |
|
|
|
fun removeFooterView(footer: View) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
footerItems?.let { |
|
|
|
footerItems?.let { |
|
|
|
val index = it.indexOfValue(footer) |
|
|
|
val index = it.indexOfValue(footer) |
|
|
@ -125,7 +123,6 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun setItems(items: List<ITEM>?) { |
|
|
|
fun setItems(items: List<ITEM>?) { |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
@ -228,7 +225,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun updateItem(item: ITEM) { |
|
|
|
fun updateItem(item: ITEM) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val index = this.items.indexOf(item) |
|
|
|
val index = this.items.indexOf(item) |
|
|
|
if (index >= 0) { |
|
|
|
if (index >= 0) { |
|
|
@ -236,18 +233,16 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
notifyItemChanged(index) |
|
|
|
notifyItemChanged(index) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun updateItem(position: Int, payload: Any) { |
|
|
|
fun updateItem(position: Int, payload: Any) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val size = getActualItemCount() |
|
|
|
val size = getActualItemCount() |
|
|
|
if (position in 0 until size) { |
|
|
|
if (position in 0 until size) { |
|
|
|
notifyItemChanged(position + getHeaderCount(), payload) |
|
|
|
notifyItemChanged(position + getHeaderCount(), payload) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) { |
|
|
|
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
val size = getActualItemCount() |
|
|
|
val size = getActualItemCount() |
|
|
|
if (fromPosition in 0 until size && toPosition in 0 until size) { |
|
|
|
if (fromPosition in 0 until size && toPosition in 0 until size) { |
|
|
@ -258,118 +253,93 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun clearItems() { |
|
|
|
fun clearItems() = |
|
|
|
synchronized(lock) { |
|
|
|
synchronized(lock) { |
|
|
|
this.items.clear() |
|
|
|
this.items.clear() |
|
|
|
notifyDataSetChanged() |
|
|
|
notifyDataSetChanged() |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun isEmpty(): Boolean { |
|
|
|
fun isEmpty() = items.isEmpty() |
|
|
|
return items.isEmpty() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun isNotEmpty(): Boolean { |
|
|
|
fun isNotEmpty() = items.isNotEmpty() |
|
|
|
return items.isNotEmpty() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 除去header和footer |
|
|
|
* 除去header和footer |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
fun getActualItemCount(): Int { |
|
|
|
fun getActualItemCount() = items.size |
|
|
|
return items.size |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getHeaderCount(): Int { |
|
|
|
|
|
|
|
return headerItems?.size() ?: 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getFooterCount(): Int { |
|
|
|
fun getHeaderCount() = headerItems?.size() ?: 0 |
|
|
|
return footerItems?.size() ?: 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun getFooterCount() = footerItems?.size() ?: 0 |
|
|
|
|
|
|
|
|
|
|
|
fun getItem(position: Int): ITEM? = items.getOrNull(position) |
|
|
|
fun getItem(position: Int): ITEM? = items.getOrNull(position) |
|
|
|
|
|
|
|
|
|
|
|
fun getItemByLayoutPosition(position: Int): ITEM? { |
|
|
|
fun getItemByLayoutPosition(position: Int) = items.getOrNull(position - getHeaderCount()) |
|
|
|
val pos = position - getHeaderCount() |
|
|
|
|
|
|
|
return items.getOrNull(pos) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fun getItems(): List<ITEM> = items |
|
|
|
fun getItems(): List<ITEM> = items |
|
|
|
|
|
|
|
|
|
|
|
protected open fun getItemViewType(item: ITEM, position: Int): Int { |
|
|
|
protected open fun getItemViewType(item: ITEM, position: Int) = 0 |
|
|
|
return 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* grid 模式下使用 |
|
|
|
* grid 模式下使用 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
protected open fun getSpanSize(item: ITEM, viewType: Int, position: Int): Int { |
|
|
|
protected open fun getSpanSize(item: ITEM, viewType: Int, position: Int) = 1 |
|
|
|
return 1 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun getItemCount(): Int { |
|
|
|
final override fun getItemCount() = getActualItemCount() + getHeaderCount() + getFooterCount() |
|
|
|
return getActualItemCount() + getHeaderCount() + getFooterCount() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun getItemViewType(position: Int): Int { |
|
|
|
final override fun getItemViewType(position: Int) = when { |
|
|
|
return when { |
|
|
|
isHeader(position) -> TYPE_HEADER_VIEW + position |
|
|
|
isHeader(position) -> TYPE_HEADER_VIEW + position |
|
|
|
isFooter(position) -> TYPE_FOOTER_VIEW + position - getActualItemCount() - getHeaderCount() |
|
|
|
isFooter(position) -> TYPE_FOOTER_VIEW + position - getActualItemCount() - getHeaderCount() |
|
|
|
else -> getItem(getActualPosition(position))?.let { |
|
|
|
else -> getItem(getActualPosition(position))?.let { |
|
|
|
getItemViewType(it, getActualPosition(position)) |
|
|
|
getItemViewType(it, getActualPosition(position)) |
|
|
|
} ?: 0 |
|
|
|
} ?: 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { |
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when { |
|
|
|
return when { |
|
|
|
viewType < TYPE_HEADER_VIEW + getHeaderCount() -> { |
|
|
|
viewType < TYPE_HEADER_VIEW + getHeaderCount() -> { |
|
|
|
ItemViewHolder(headerItems!!.get(viewType)) |
|
|
|
ItemViewHolder(headerItems!!.get(viewType)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
viewType >= TYPE_FOOTER_VIEW -> { |
|
|
|
viewType >= TYPE_FOOTER_VIEW -> { |
|
|
|
ItemViewHolder(footerItems!!.get(viewType)) |
|
|
|
ItemViewHolder(footerItems!!.get(viewType)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
else -> { |
|
|
|
else -> { |
|
|
|
val holder = ItemViewHolder( |
|
|
|
val holder = ItemViewHolder( |
|
|
|
inflater.inflate( |
|
|
|
inflater.inflate( |
|
|
|
itemDelegates.getValue(viewType).layoutId, |
|
|
|
itemDelegates.getValue(viewType).layoutId, |
|
|
|
parent, |
|
|
|
parent, |
|
|
|
false |
|
|
|
false |
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
itemDelegates.getValue(viewType) |
|
|
|
itemDelegates.getValue(viewType) |
|
|
|
.registerListener(holder) |
|
|
|
.registerListener(holder) |
|
|
|
|
|
|
|
|
|
|
|
if (itemClickListener != null) { |
|
|
|
if (itemClickListener != null) { |
|
|
|
holder.itemView.setOnClickListener { |
|
|
|
holder.itemView.setOnClickListener { |
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
itemClickListener?.invoke(holder, it) |
|
|
|
itemClickListener?.invoke(holder, it) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (itemLongClickListener != null) { |
|
|
|
if (itemLongClickListener != null) { |
|
|
|
holder.itemView.setOnLongClickListener { |
|
|
|
holder.itemView.setOnLongClickListener { |
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
getItem(holder.layoutPosition)?.let { |
|
|
|
itemLongClickListener?.invoke(holder, it) ?: true |
|
|
|
itemLongClickListener?.invoke(holder, it) ?: true |
|
|
|
} ?: true |
|
|
|
} ?: true |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
holder |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
holder |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {} |
|
|
|
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final override fun onBindViewHolder( |
|
|
|
final override fun onBindViewHolder( |
|
|
|
holder: ItemViewHolder, |
|
|
|
holder: ItemViewHolder, |
|
|
@ -395,7 +365,7 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
super.onAttachedToRecyclerView(recyclerView) |
|
|
|
super.onAttachedToRecyclerView(recyclerView) |
|
|
|
val manager = recyclerView.layoutManager |
|
|
|
val manager = recyclerView.layoutManager |
|
|
|
if (manager is GridLayoutManager) { |
|
|
|
if (manager is GridLayoutManager) { |
|
|
|
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { |
|
|
|
manager.spanSizeLookup = object: GridLayoutManager.SpanSizeLookup() { |
|
|
|
override fun getSpanSize(position: Int): Int { |
|
|
|
override fun getSpanSize(position: Int): Int { |
|
|
|
return getItem(position)?.let { |
|
|
|
return getItem(position)?.let { |
|
|
|
if (isHeader(position) || isFooter(position)) manager.spanCount else getSpanSize( |
|
|
|
if (isHeader(position) || isFooter(position)) manager.spanCount else getSpanSize( |
|
|
@ -407,21 +377,11 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fun setItemAnimation(item: ItemAnimation) { |
|
|
|
private fun isHeader(position: Int) = position < getHeaderCount() |
|
|
|
itemAnimation = item |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun isHeader(position: Int): Boolean { |
|
|
|
private fun isFooter(position: Int) = position >= getActualItemCount() + getHeaderCount() |
|
|
|
return position < getHeaderCount() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun isFooter(position: Int): Boolean { |
|
|
|
private fun getActualPosition(position: Int) = position - getHeaderCount() |
|
|
|
return position >= getActualItemCount() + getHeaderCount() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun getActualPosition(position: Int): Int { |
|
|
|
|
|
|
|
return position - getHeaderCount() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun addAnimation(holder: ItemViewHolder) { |
|
|
|
private fun addAnimation(holder: ItemViewHolder) { |
|
|
|
itemAnimation?.let { |
|
|
|
itemAnimation?.let { |
|
|
@ -434,7 +394,6 @@ abstract class CommonRecyclerAdapter<ITEM>(protected val context: Context) : |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { |
|
|
|
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { |
|
|
|
item.itemAnimation?.let { |
|
|
|
item.itemAnimation?.let { |
|
|
|
for (anim in it.getAnimators(holder.itemView)) { |
|
|
|
for (anim in it.getAnimators(holder.itemView)) { |
|
|
|