parent
378d2e56d1
commit
08d0a46204
@ -0,0 +1,28 @@ |
||||
package xyz.fycz.myreader.base.adapter2 |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/8/20 17:13 |
||||
*/ |
||||
|
||||
import android.view.View |
||||
|
||||
/** |
||||
* Registers the [block] lambda as [View.OnClickListener] to this View. |
||||
* |
||||
* If this View is not clickable, it becomes clickable. |
||||
*/ |
||||
inline fun View.onClick(crossinline block: () -> Unit) = setOnClickListener { block() } |
||||
|
||||
/** |
||||
* Register the [block] lambda as [View.OnLongClickListener] to this View. |
||||
* By default, [consume] is set to true because it's the most common use case, but you can set it |
||||
* to false. |
||||
* If you want to return a value dynamically, use [View.setOnLongClickListener] instead. |
||||
* |
||||
* If this view is not long clickable, it becomes long clickable. |
||||
*/ |
||||
inline fun View.onLongClick( |
||||
consume: Boolean = true, |
||||
crossinline block: () -> Unit |
||||
) = setOnLongClickListener { block(); consume } |
@ -0,0 +1,215 @@ |
||||
package xyz.fycz.myreader.base.adapter2 |
||||
|
||||
import android.content.Context |
||||
import android.view.LayoutInflater |
||||
import android.view.ViewGroup |
||||
import androidx.recyclerview.widget.AsyncListDiffer |
||||
import androidx.recyclerview.widget.DiffUtil |
||||
import androidx.recyclerview.widget.GridLayoutManager |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import androidx.viewbinding.ViewBinding |
||||
|
||||
/** |
||||
* Created by Invincible on 2017/12/15. |
||||
*/ |
||||
@Suppress("unused", "MemberVisibilityCanBePrivate") |
||||
abstract class DiffRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) : |
||||
RecyclerView.Adapter<ItemViewHolder>() { |
||||
|
||||
val inflater: LayoutInflater = LayoutInflater.from(context) |
||||
|
||||
private val asyncListDiffer: AsyncListDiffer<ITEM> by lazy { |
||||
AsyncListDiffer(this, diffItemCallback).apply { |
||||
addListListener { _, _ -> |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
} |
||||
|
||||
private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null |
||||
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null |
||||
|
||||
var itemAnimation: ItemAnimation? = null |
||||
|
||||
abstract val diffItemCallback: DiffUtil.ItemCallback<ITEM> |
||||
|
||||
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 |
||||
} |
||||
|
||||
@Synchronized |
||||
fun setItems(items: List<ITEM>?) { |
||||
kotlin.runCatching { |
||||
asyncListDiffer.submitList(items) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun setItem(position: Int, item: ITEM) { |
||||
kotlin.runCatching { |
||||
val list = ArrayList(asyncListDiffer.currentList) |
||||
list[position] = item |
||||
asyncListDiffer.submitList(list) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItem(item: ITEM) { |
||||
kotlin.runCatching { |
||||
val index = asyncListDiffer.currentList.indexOf(item) |
||||
if (index >= 0) { |
||||
asyncListDiffer.currentList[index] = item |
||||
notifyItemChanged(index) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItem(position: Int, payload: Any) { |
||||
kotlin.runCatching { |
||||
val size = itemCount |
||||
if (position in 0 until size) { |
||||
notifyItemChanged(position, payload) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) { |
||||
kotlin.runCatching { |
||||
val size = itemCount |
||||
if (fromPosition in 0 until size && toPosition in 0 until size) { |
||||
notifyItemRangeChanged( |
||||
fromPosition, |
||||
toPosition - fromPosition + 1, |
||||
payloads |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun isEmpty() = asyncListDiffer.currentList.isEmpty() |
||||
|
||||
fun isNotEmpty() = asyncListDiffer.currentList.isNotEmpty() |
||||
|
||||
fun getItem(position: Int): ITEM? = asyncListDiffer.currentList.getOrNull(position) |
||||
|
||||
fun getItems(): List<ITEM> = asyncListDiffer.currentList |
||||
|
||||
/** |
||||
* grid 模式下使用 |
||||
*/ |
||||
protected open fun getSpanSize(viewType: Int, position: Int) = 1 |
||||
|
||||
final override fun getItemCount() = getItems().size |
||||
|
||||
final override fun getItemViewType(position: Int): Int { |
||||
return 0 |
||||
} |
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder { |
||||
val holder = ItemViewHolder(getViewBinding(parent)) |
||||
|
||||
@Suppress("UNCHECKED_CAST") |
||||
registerListener(holder, (holder.binding as VB)) |
||||
|
||||
if (itemClickListener != null) { |
||||
holder.itemView.setOnClickListener { |
||||
getItem(holder.layoutPosition)?.let { |
||||
itemClickListener?.invoke(holder, it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (itemLongClickListener != null) { |
||||
holder.itemView.onLongClick { |
||||
getItem(holder.layoutPosition)?.let { |
||||
itemLongClickListener?.invoke(holder, it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
return holder |
||||
} |
||||
|
||||
protected abstract fun getViewBinding(parent: ViewGroup): VB |
||||
|
||||
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {} |
||||
|
||||
open fun onCurrentListChanged() { |
||||
|
||||
} |
||||
|
||||
@Suppress("UNCHECKED_CAST") |
||||
final override fun onBindViewHolder( |
||||
holder: ItemViewHolder, |
||||
position: Int, |
||||
payloads: MutableList<Any> |
||||
) { |
||||
getItem(holder.layoutPosition)?.let { |
||||
convert(holder, (holder.binding as VB), it, payloads) |
||||
} |
||||
} |
||||
|
||||
override fun onViewAttachedToWindow(holder: ItemViewHolder) { |
||||
super.onViewAttachedToWindow(holder) |
||||
addAnimation(holder) |
||||
} |
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { |
||||
super.onAttachedToRecyclerView(recyclerView) |
||||
val manager = recyclerView.layoutManager |
||||
if (manager is GridLayoutManager) { |
||||
manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { |
||||
override fun getSpanSize(position: Int): Int { |
||||
return getSpanSize(getItemViewType(position), position) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun addAnimation(holder: ItemViewHolder) { |
||||
itemAnimation?.let { |
||||
if (it.itemAnimEnabled) { |
||||
if (!it.itemAnimFirstOnly || holder.layoutPosition > it.itemAnimStartPosition) { |
||||
startAnimation(holder, it) |
||||
it.itemAnimStartPosition = holder.layoutPosition |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { |
||||
item.itemAnimation?.let { |
||||
for (anim in it.getAnimators(holder.itemView)) { |
||||
anim.setDuration(item.itemAnimDuration).start() |
||||
anim.interpolator = item.itemAnimInterpolator |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 如果使用了事件回调,回调里不要直接使用item,会出现不更新的问题, |
||||
* 使用getItem(holder.layoutPosition)来获取item |
||||
*/ |
||||
abstract fun convert( |
||||
holder: ItemViewHolder, |
||||
binding: VB, |
||||
item: ITEM, |
||||
payloads: MutableList<Any> |
||||
) |
||||
|
||||
/** |
||||
* 注册事件 |
||||
*/ |
||||
abstract fun registerListener(holder: ItemViewHolder, binding: VB) |
||||
|
||||
} |
@ -0,0 +1,80 @@ |
||||
package xyz.fycz.myreader.base.adapter2 |
||||
|
||||
import android.view.animation.Interpolator |
||||
import android.view.animation.LinearInterpolator |
||||
import xyz.fycz.myreader.base.adapter2.animations.* |
||||
|
||||
/** |
||||
* Created by Invincible on 2017/12/15. |
||||
*/ |
||||
@Suppress("unused") |
||||
class ItemAnimation private constructor() { |
||||
|
||||
var itemAnimEnabled = false |
||||
var itemAnimFirstOnly = true |
||||
var itemAnimation: BaseAnimation? = null |
||||
var itemAnimInterpolator: Interpolator = LinearInterpolator() |
||||
var itemAnimDuration: Long = 300L |
||||
var itemAnimStartPosition: Int = -1 |
||||
|
||||
fun interpolator(interpolator: Interpolator) = apply { |
||||
itemAnimInterpolator = interpolator |
||||
} |
||||
|
||||
fun duration(duration: Long) = apply { |
||||
itemAnimDuration = duration |
||||
} |
||||
|
||||
fun startPosition(startPos: Int) = apply { |
||||
itemAnimStartPosition = startPos |
||||
} |
||||
|
||||
fun animation(animationType: Int = NONE, animation: BaseAnimation? = null) = apply { |
||||
if (animation != null) { |
||||
itemAnimation = animation |
||||
} else { |
||||
when (animationType) { |
||||
FADE_IN -> itemAnimation = AlphaInAnimation() |
||||
SCALE_IN -> itemAnimation = ScaleInAnimation() |
||||
BOTTOM_SLIDE_IN -> itemAnimation = SlideInBottomAnimation() |
||||
LEFT_SLIDE_IN -> itemAnimation = SlideInLeftAnimation() |
||||
RIGHT_SLIDE_IN -> itemAnimation = SlideInRightAnimation() |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun enabled(enabled: Boolean) = apply { |
||||
itemAnimEnabled = enabled |
||||
} |
||||
|
||||
fun firstOnly(firstOnly: Boolean) = apply { |
||||
itemAnimFirstOnly = firstOnly |
||||
} |
||||
|
||||
companion object { |
||||
const val NONE: Int = 0x00000000 |
||||
/** |
||||
* Use with [.openLoadAnimation] |
||||
*/ |
||||
const val FADE_IN: Int = 0x00000001 |
||||
/** |
||||
* Use with [.openLoadAnimation] |
||||
*/ |
||||
const val SCALE_IN: Int = 0x00000002 |
||||
/** |
||||
* Use with [.openLoadAnimation] |
||||
*/ |
||||
const val BOTTOM_SLIDE_IN: Int = 0x00000003 |
||||
/** |
||||
* Use with [.openLoadAnimation] |
||||
*/ |
||||
const val LEFT_SLIDE_IN: Int = 0x00000004 |
||||
/** |
||||
* Use with [.openLoadAnimation] |
||||
*/ |
||||
const val RIGHT_SLIDE_IN: Int = 0x00000005 |
||||
|
||||
fun create() = ItemAnimation() |
||||
|
||||
} |
||||
} |
@ -0,0 +1,10 @@ |
||||
package xyz.fycz.myreader.base.adapter2 |
||||
|
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import androidx.viewbinding.ViewBinding |
||||
|
||||
/** |
||||
* Created by Invincible on 2017/11/28. |
||||
*/ |
||||
@Suppress("MemberVisibilityCanBePrivate") |
||||
class ItemViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) |
@ -0,0 +1,452 @@ |
||||
package xyz.fycz.myreader.base.adapter2 |
||||
|
||||
import android.content.Context |
||||
import android.util.SparseArray |
||||
import android.view.LayoutInflater |
||||
import android.view.ViewGroup |
||||
import androidx.recyclerview.widget.DiffUtil |
||||
import androidx.recyclerview.widget.GridLayoutManager |
||||
import androidx.recyclerview.widget.RecyclerView |
||||
import androidx.viewbinding.ViewBinding |
||||
import java.util.* |
||||
|
||||
/** |
||||
* Created by Invincible on 2017/11/24. |
||||
* |
||||
* 通用的adapter 可添加header,footer,以及不同类型item |
||||
*/ |
||||
@Suppress("unused", "MemberVisibilityCanBePrivate") |
||||
abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Context) : |
||||
RecyclerView.Adapter<ItemViewHolder>() { |
||||
|
||||
val inflater: LayoutInflater = LayoutInflater.from(context) |
||||
|
||||
private val headerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
||||
private val footerItems: SparseArray<(parent: ViewGroup) -> ViewBinding> by lazy { SparseArray() } |
||||
|
||||
private val items: MutableList<ITEM> = mutableListOf() |
||||
|
||||
private var itemClickListener: ((holder: ItemViewHolder, item: ITEM) -> Unit)? = null |
||||
private var itemLongClickListener: ((holder: ItemViewHolder, item: ITEM) -> Boolean)? = null |
||||
|
||||
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 |
||||
} |
||||
|
||||
@Synchronized |
||||
fun addHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { |
||||
kotlin.runCatching { |
||||
val index = headerItems.size() |
||||
headerItems.put(TYPE_HEADER_VIEW + headerItems.size(), header) |
||||
notifyItemInserted(index) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun addFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) { |
||||
kotlin.runCatching { |
||||
val index = getActualItemCount() + footerItems.size() |
||||
footerItems.put(TYPE_FOOTER_VIEW + footerItems.size(), footer) |
||||
notifyItemInserted(index) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun removeHeaderView(header: ((parent: ViewGroup) -> ViewBinding)) { |
||||
kotlin.runCatching { |
||||
val index = headerItems.indexOfValue(header) |
||||
if (index >= 0) { |
||||
headerItems.remove(index) |
||||
notifyItemRemoved(index) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun removeFooterView(footer: ((parent: ViewGroup) -> ViewBinding)) { |
||||
kotlin.runCatching { |
||||
val index = footerItems.indexOfValue(footer) |
||||
if (index >= 0) { |
||||
footerItems.remove(index) |
||||
notifyItemRemoved(getActualItemCount() + index - 2) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun setItems(items: List<ITEM>?) { |
||||
kotlin.runCatching { |
||||
if (this.items.isNotEmpty()) { |
||||
this.items.clear() |
||||
} |
||||
if (items != null) { |
||||
this.items.addAll(items) |
||||
} |
||||
notifyDataSetChanged() |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun setItems(items: List<ITEM>?, itemCallback: DiffUtil.ItemCallback<ITEM>) { |
||||
kotlin.runCatching { |
||||
val callback = object : DiffUtil.Callback() { |
||||
override fun getOldListSize(): Int { |
||||
return itemCount |
||||
} |
||||
|
||||
override fun getNewListSize(): Int { |
||||
return (items?.size ?: 0) + getHeaderCount() + getFooterCount() |
||||
} |
||||
|
||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { |
||||
val oldItem = getItem(oldItemPosition - getHeaderCount()) |
||||
?: return true |
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount()) |
||||
?: return true |
||||
return itemCallback.areItemsTheSame(oldItem, newItem) |
||||
} |
||||
|
||||
override fun areContentsTheSame( |
||||
oldItemPosition: Int, |
||||
newItemPosition: Int |
||||
): Boolean { |
||||
val oldItem = getItem(oldItemPosition - getHeaderCount()) |
||||
?: return true |
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount()) |
||||
?: return true |
||||
return itemCallback.areContentsTheSame(oldItem, newItem) |
||||
} |
||||
|
||||
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { |
||||
val oldItem = getItem(oldItemPosition - getHeaderCount()) |
||||
?: return null |
||||
val newItem = items?.getOrNull(newItemPosition - getHeaderCount()) |
||||
?: return null |
||||
return itemCallback.getChangePayload(oldItem, newItem) |
||||
} |
||||
} |
||||
val diffResult = DiffUtil.calculateDiff(callback) |
||||
if (this.items.isNotEmpty()) { |
||||
this.items.clear() |
||||
} |
||||
if (items != null) { |
||||
this.items.addAll(items) |
||||
} |
||||
diffResult.dispatchUpdatesTo(this) |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun setItem(position: Int, item: ITEM) { |
||||
kotlin.runCatching { |
||||
val oldSize = getActualItemCount() |
||||
if (position in 0 until oldSize) { |
||||
this.items[position] = item |
||||
notifyItemChanged(position + getHeaderCount()) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun addItem(item: ITEM) { |
||||
kotlin.runCatching { |
||||
val oldSize = getActualItemCount() |
||||
if (this.items.add(item)) { |
||||
notifyItemInserted(oldSize + getHeaderCount()) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun addItems(position: Int, newItems: List<ITEM>) { |
||||
kotlin.runCatching { |
||||
if (this.items.addAll(position, newItems)) { |
||||
notifyItemRangeInserted(position + getHeaderCount(), newItems.size) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun addItems(newItems: List<ITEM>) { |
||||
kotlin.runCatching { |
||||
val oldSize = getActualItemCount() |
||||
if (this.items.addAll(newItems)) { |
||||
if (oldSize == 0 && getHeaderCount() == 0) { |
||||
notifyDataSetChanged() |
||||
} else { |
||||
notifyItemRangeInserted(oldSize + getHeaderCount(), newItems.size) |
||||
} |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun removeItem(position: Int) { |
||||
kotlin.runCatching { |
||||
if (this.items.removeAt(position) != null) { |
||||
notifyItemRemoved(position + getHeaderCount()) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun removeItem(item: ITEM) { |
||||
kotlin.runCatching { |
||||
if (this.items.remove(item)) { |
||||
notifyItemRemoved(this.items.indexOf(item) + getHeaderCount()) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun removeItems(items: List<ITEM>) { |
||||
kotlin.runCatching { |
||||
if (this.items.removeAll(items)) { |
||||
notifyDataSetChanged() |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun swapItem(oldPosition: Int, newPosition: Int) { |
||||
kotlin.runCatching { |
||||
val size = getActualItemCount() |
||||
if (oldPosition in 0 until size && newPosition in 0 until size) { |
||||
val srcPosition = oldPosition + getHeaderCount() |
||||
val targetPosition = newPosition + getHeaderCount() |
||||
Collections.swap(this.items, srcPosition, targetPosition) |
||||
notifyItemMoved(srcPosition, targetPosition) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItem(item: ITEM) { |
||||
kotlin.runCatching { |
||||
val index = this.items.indexOf(item) |
||||
if (index >= 0) { |
||||
this.items[index] = item |
||||
notifyItemChanged(index) |
||||
} |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItem(position: Int, payload: Any) { |
||||
kotlin.runCatching { |
||||
val size = getActualItemCount() |
||||
if (position in 0 until size) { |
||||
notifyItemChanged(position + getHeaderCount(), payload) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun updateItems(fromPosition: Int, toPosition: Int, payloads: Any) { |
||||
kotlin.runCatching { |
||||
val size = getActualItemCount() |
||||
if (fromPosition in 0 until size && toPosition in 0 until size) { |
||||
notifyItemRangeChanged( |
||||
fromPosition + getHeaderCount(), |
||||
toPosition - fromPosition + 1, |
||||
payloads |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
fun clearItems() { |
||||
kotlin.runCatching { |
||||
this.items.clear() |
||||
notifyDataSetChanged() |
||||
onCurrentListChanged() |
||||
} |
||||
} |
||||
|
||||
fun isEmpty() = items.isEmpty() |
||||
|
||||
fun isNotEmpty() = items.isNotEmpty() |
||||
|
||||
/** |
||||
* 除去header和footer |
||||
*/ |
||||
fun getActualItemCount() = items.size |
||||
|
||||
|
||||
fun getHeaderCount() = headerItems.size() |
||||
|
||||
|
||||
fun getFooterCount() = footerItems.size() |
||||
|
||||
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(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() |
||||
else -> getItem(getActualPosition(position))?.let { |
||||
getItemViewType(it, getActualPosition(position)) |
||||
} ?: 0 |
||||
} |
||||
|
||||
open fun onCurrentListChanged() { |
||||
|
||||
} |
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when { |
||||
viewType < TYPE_HEADER_VIEW + getHeaderCount() -> { |
||||
ItemViewHolder(headerItems.get(viewType).invoke(parent)) |
||||
} |
||||
|
||||
viewType >= TYPE_FOOTER_VIEW -> { |
||||
ItemViewHolder(footerItems.get(viewType).invoke(parent)) |
||||
} |
||||
|
||||
else -> { |
||||
val holder = ItemViewHolder(getViewBinding(parent)) |
||||
|
||||
@Suppress("UNCHECKED_CAST") |
||||
registerListener(holder, (holder.binding as VB)) |
||||
|
||||
if (itemClickListener != null) { |
||||
holder.itemView.setOnClickListener { |
||||
getItem(holder.layoutPosition)?.let { |
||||
itemClickListener?.invoke(holder, it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (itemLongClickListener != null) { |
||||
holder.itemView.onLongClick { |
||||
getItem(holder.layoutPosition)?.let { |
||||
itemLongClickListener?.invoke(holder, it) |
||||
} |
||||
} |
||||
} |
||||
|
||||
holder |
||||
} |
||||
} |
||||
|
||||
protected abstract fun getViewBinding(parent: ViewGroup): VB |
||||
|
||||
final override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {} |
||||
|
||||
@Suppress("UNCHECKED_CAST") |
||||
final override fun onBindViewHolder( |
||||
holder: ItemViewHolder, |
||||
position: Int, |
||||
payloads: MutableList<Any> |
||||
) { |
||||
if (!isHeader(holder.layoutPosition) && !isFooter(holder.layoutPosition)) { |
||||
getItem(holder.layoutPosition - getHeaderCount())?.let { |
||||
convert(holder, (holder.binding as VB), it, payloads) |
||||
} |
||||
} |
||||
} |
||||
|
||||
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() { |
||||
override fun getSpanSize(position: Int): Int { |
||||
return getSpanSize(getItemViewType(position), position) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
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) { |
||||
if (!it.itemAnimFirstOnly || holder.layoutPosition > it.itemAnimStartPosition) { |
||||
startAnimation(holder, it) |
||||
it.itemAnimStartPosition = holder.layoutPosition |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
protected open fun startAnimation(holder: ItemViewHolder, item: ItemAnimation) { |
||||
item.itemAnimation?.let { |
||||
for (anim in it.getAnimators(holder.itemView)) { |
||||
anim.setDuration(item.itemAnimDuration).start() |
||||
anim.interpolator = item.itemAnimInterpolator |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 如果使用了事件回调,回调里不要直接使用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 { |
||||
private const val TYPE_HEADER_VIEW = Int.MIN_VALUE |
||||
private const val TYPE_FOOTER_VIEW = Int.MAX_VALUE - 999 |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,18 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ObjectAnimator |
||||
import android.view.View |
||||
|
||||
|
||||
class AlphaInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_ALPHA_FROM) : |
||||
BaseAnimation { |
||||
|
||||
override fun getAnimators(view: View): Array<Animator> = |
||||
arrayOf(ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f)) |
||||
|
||||
companion object { |
||||
|
||||
private const val DEFAULT_ALPHA_FROM = 0f |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.view.View |
||||
|
||||
/** |
||||
* adapter item 动画 |
||||
*/ |
||||
interface BaseAnimation { |
||||
|
||||
fun getAnimators(view: View): Array<Animator> |
||||
|
||||
} |
@ -0,0 +1,21 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ObjectAnimator |
||||
import android.view.View |
||||
|
||||
|
||||
class ScaleInAnimation @JvmOverloads constructor(private val mFrom: Float = DEFAULT_SCALE_FROM) : |
||||
BaseAnimation { |
||||
|
||||
override fun getAnimators(view: View): Array<Animator> { |
||||
val scaleX = ObjectAnimator.ofFloat(view, "scaleX", mFrom, 1f) |
||||
val scaleY = ObjectAnimator.ofFloat(view, "scaleY", mFrom, 1f) |
||||
return arrayOf(scaleX, scaleY) |
||||
} |
||||
|
||||
companion object { |
||||
|
||||
private const val DEFAULT_SCALE_FROM = .5f |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ObjectAnimator |
||||
import android.view.View |
||||
import xyz.fycz.myreader.base.adapter2.animations.BaseAnimation |
||||
|
||||
class SlideInBottomAnimation : BaseAnimation { |
||||
|
||||
|
||||
override fun getAnimators(view: View): Array<Animator> = |
||||
arrayOf(ObjectAnimator.ofFloat(view, "translationY", view.measuredHeight.toFloat(), 0f)) |
||||
} |
@ -0,0 +1,14 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ObjectAnimator |
||||
import android.view.View |
||||
import xyz.fycz.myreader.base.adapter2.animations.BaseAnimation |
||||
|
||||
|
||||
class SlideInLeftAnimation : BaseAnimation { |
||||
|
||||
|
||||
override fun getAnimators(view: View): Array<Animator> = |
||||
arrayOf(ObjectAnimator.ofFloat(view, "translationX", -view.rootView.width.toFloat(), 0f)) |
||||
} |
@ -0,0 +1,14 @@ |
||||
package xyz.fycz.myreader.base.adapter2.animations |
||||
|
||||
import android.animation.Animator |
||||
import android.animation.ObjectAnimator |
||||
import android.view.View |
||||
import xyz.fycz.myreader.base.adapter2.animations.BaseAnimation |
||||
|
||||
|
||||
class SlideInRightAnimation : BaseAnimation { |
||||
|
||||
|
||||
override fun getAnimators(view: View): Array<Animator> = |
||||
arrayOf(ObjectAnimator.ofFloat(view, "translationX", view.rootView.width.toFloat(), 0f)) |
||||
} |
@ -0,0 +1,432 @@ |
||||
package xyz.fycz.myreader.ui.adapter |
||||
|
||||
import android.content.Context |
||||
import android.content.Intent |
||||
import android.os.Bundle |
||||
import android.os.Handler |
||||
import android.os.Looper |
||||
import android.text.TextUtils |
||||
import android.util.Log |
||||
import android.view.View |
||||
import android.view.ViewGroup |
||||
import androidx.recyclerview.widget.DiffUtil |
||||
import xyz.fycz.myreader.R |
||||
import xyz.fycz.myreader.application.App |
||||
import xyz.fycz.myreader.application.SysManager |
||||
import xyz.fycz.myreader.base.BitIntentDataManager |
||||
import xyz.fycz.myreader.base.adapter2.DiffRecyclerAdapter |
||||
import xyz.fycz.myreader.base.adapter2.ItemViewHolder |
||||
import xyz.fycz.myreader.base.adapter2.onClick |
||||
import xyz.fycz.myreader.databinding.SearchBookItemBinding |
||||
import xyz.fycz.myreader.entity.SearchBookBean |
||||
import xyz.fycz.myreader.greendao.entity.Book |
||||
import xyz.fycz.myreader.model.SearchEngine |
||||
import xyz.fycz.myreader.model.SearchEngine.OnGetBookInfoListener |
||||
import xyz.fycz.myreader.model.mulvalmap.ConMVMap |
||||
import xyz.fycz.myreader.model.sourceAnalyzer.BookSourceManager |
||||
import xyz.fycz.myreader.ui.activity.BookDetailedActivity |
||||
import xyz.fycz.myreader.util.help.StringHelper |
||||
import xyz.fycz.myreader.util.utils.KeyWordUtils |
||||
import xyz.fycz.myreader.util.utils.NetworkUtils |
||||
import xyz.fycz.myreader.util.utils.StringUtils |
||||
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil |
||||
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler |
||||
|
||||
/** |
||||
* @author fengyue |
||||
* @date 2021/8/20 17:19 |
||||
*/ |
||||
|
||||
class SearchAdapter( |
||||
context: Context, |
||||
val keyword: String, |
||||
private val searchEngine: SearchEngine |
||||
) : DiffRecyclerAdapter<SearchBookBean, SearchBookItemBinding>(context) { |
||||
private val mBooks: ConMVMap<SearchBookBean, Book> = ConMVMap() |
||||
private var mList: List<SearchBookBean> = ArrayList() |
||||
private val tagList: MutableList<String> = ArrayList() |
||||
private val handler = Handler(Looper.getMainLooper()) |
||||
private var postTime = 0L |
||||
private val sendRunnable = Runnable { upAdapter() } |
||||
|
||||
override val diffItemCallback: DiffUtil.ItemCallback<SearchBookBean> |
||||
get() = object : DiffUtil.ItemCallback<SearchBookBean>() { |
||||
override fun areItemsTheSame( |
||||
oldItem: SearchBookBean, |
||||
newItem: SearchBookBean |
||||
): Boolean { |
||||
return when { |
||||
oldItem.name != newItem.name -> false |
||||
oldItem.author != newItem.author -> false |
||||
else -> true |
||||
} |
||||
} |
||||
|
||||
override fun areContentsTheSame( |
||||
oldItem: SearchBookBean, |
||||
newItem: SearchBookBean |
||||
): Boolean { |
||||
return false |
||||
} |
||||
|
||||
override fun getChangePayload(oldItem: SearchBookBean, newItem: SearchBookBean): Any { |
||||
val payload = Bundle() |
||||
payload.putInt("sourceCount", newItem.sourceCount) |
||||
if (oldItem.imgUrl != newItem.imgUrl) |
||||
payload.putString("imgUrl", newItem.imgUrl) |
||||
if (oldItem.type != newItem.type) |
||||
payload.putString("type", newItem.type) |
||||
if (oldItem.status != newItem.status) |
||||
payload.putString("status", newItem.status) |
||||
if (oldItem.wordCount != newItem.wordCount) |
||||
payload.putString("wordCount", newItem.wordCount) |
||||
if (oldItem.lastChapter != newItem.lastChapter) |
||||
payload.putString("last", newItem.lastChapter) |
||||
if (oldItem.desc != newItem.desc) |
||||
payload.putString("desc", newItem.desc) |
||||
return payload |
||||
} |
||||
} |
||||
|
||||
override fun getViewBinding(parent: ViewGroup): SearchBookItemBinding { |
||||
return SearchBookItemBinding.inflate(inflater, parent, false) |
||||
} |
||||
|
||||
override fun convert( |
||||
holder: ItemViewHolder, |
||||
binding: SearchBookItemBinding, |
||||
item: SearchBookBean, |
||||
payloads: MutableList<Any> |
||||
) { |
||||
val payload = payloads.getOrNull(0) as? Bundle |
||||
if (payload == null) { |
||||
bind(binding, item) |
||||
} else { |
||||
val books = mBooks.getValues(item) |
||||
books2SearchBookBean(item, books) |
||||
bindChange(binding, item, payload) |
||||
} |
||||
} |
||||
|
||||
private fun bind(binding: SearchBookItemBinding, data: SearchBookBean) { |
||||
var aBooks = mBooks.getValues(data) |
||||
if (aBooks == null || aBooks.size == 0) { |
||||
aBooks = ArrayList() |
||||
aBooks.add(searchBookBean2Book(data)) |
||||
} |
||||
val book = aBooks.getOrNull(0) ?: return |
||||
val source = BookSourceManager.getBookSourceByStr(book.source) |
||||
val rc = ReadCrawlerUtil.getReadCrawler(source) |
||||
data.sourceName = source.sourceName |
||||
books2SearchBookBean(data, aBooks) |
||||
binding.run { |
||||
if (data.imgUrl.isNullOrEmpty()) { |
||||
data.imgUrl = "" |
||||
} else { |
||||
data.imgUrl = NetworkUtils.getAbsoluteURL(rc.nameSpace, data.imgUrl) |
||||
} |
||||
ivBookImg.load(data.imgUrl, data.name, data.author) |
||||
KeyWordUtils.setKeyWord(tvBookName, data.name, keyword) |
||||
if (data.author.isNullOrEmpty()) { |
||||
data.author = "" |
||||
} else { |
||||
KeyWordUtils.setKeyWord(tvBookAuthor, data.author, keyword) |
||||
} |
||||
initTagList(this, data) |
||||
if (data.lastChapter.isNullOrEmpty()) { |
||||
data.lastChapter = "" |
||||
} else { |
||||
tvBookNewestChapter.text = context.getString( |
||||
R.string.newest_chapter, |
||||
data.lastChapter |
||||
) |
||||
} |
||||
if (data.desc.isNullOrEmpty()) { |
||||
data.desc = "" |
||||
} else { |
||||
tvBookDesc.text = String.format("简介:%s", data.desc) |
||||
} |
||||
tvBookSource.text = context.getString( |
||||
R.string.source_title_num, |
||||
data.sourceName, |
||||
data.sourceCount |
||||
) |
||||
} |
||||
App.getHandler().postDelayed({ |
||||
val url = rc.nameSpace |
||||
if (needGetInfo(data) && rc is BookInfoCrawler) { |
||||
Log.i(data.name, "initOtherInfo") |
||||
searchEngine.getBookInfo(book, rc) { isSuccess: Boolean -> |
||||
if (isSuccess) { |
||||
val books: MutableList<Book> = ArrayList() |
||||
books.add(book) |
||||
books2SearchBookBean(data, books) |
||||
val payload = Bundle() |
||||
if (!data.imgUrl.isNullOrEmpty()) |
||||
payload.putString( |
||||
"imgUrl", |
||||
NetworkUtils.getAbsoluteURL(url, data.imgUrl) |
||||
) |
||||
if (!data.type.isNullOrEmpty()) |
||||
payload.putString("type", data.type) |
||||
if (!data.status.isNullOrEmpty()) |
||||
payload.putString("status", data.status) |
||||
if (!data.wordCount.isNullOrEmpty()) |
||||
payload.putString("wordCount", data.wordCount) |
||||
if (!data.lastChapter.isNullOrEmpty()) |
||||
payload.putString("last", data.lastChapter) |
||||
if (!data.desc.isNullOrEmpty()) |
||||
payload.putString("desc", data.desc) |
||||
bindChange(binding, data, payload) |
||||
} |
||||
} |
||||
} |
||||
}, 1000) |
||||
} |
||||
|
||||
private fun initTagList(binding: SearchBookItemBinding, data: SearchBookBean) { |
||||
tagList.clear() |
||||
val type = data.type |
||||
if (!type.isNullOrEmpty()) tagList.add("0:$type") |
||||
val wordCount = data.wordCount |
||||
if (!wordCount.isNullOrEmpty()) tagList.add("1:$wordCount") |
||||
val status = data.status |
||||
if (!status.isNullOrEmpty()) tagList.add("2:$status") |
||||
binding.run { |
||||
if (tagList.size == 0) { |
||||
tflBookTag.visibility = View.GONE |
||||
} else { |
||||
tflBookTag.visibility = View.VISIBLE |
||||
tflBookTag.adapter = BookTagAdapter(context, tagList, 11) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun bindChange(binding: SearchBookItemBinding, data: SearchBookBean, payload: Bundle) { |
||||
binding.run { |
||||
initTagList(this, data) |
||||
payload.keySet().forEach { |
||||
when (it) { |
||||
"sourceCount" -> tvBookSource.text = context.getString( |
||||
R.string.source_title_num, |
||||
data.sourceName, |
||||
data.sourceCount |
||||
) |
||||
"imgUrl" -> ivBookImg.load( |
||||
data.imgUrl, |
||||
data.name, |
||||
data.author |
||||
) |
||||
"last" -> tvBookNewestChapter.text = context.getString( |
||||
R.string.newest_chapter, |
||||
data.lastChapter |
||||
) |
||||
"desc" -> tvBookDesc.text = String.format("简介:%s", data.desc) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: SearchBookItemBinding) { |
||||
binding.root.setOnClickListener { |
||||
getItem(holder.layoutPosition)?.let { |
||||
val books = mBooks.getValues(it) |
||||
if (books == null || books.size == 0) return@let |
||||
books[0] = searchBookBean2Book(it, books[0]) |
||||
val intent = Intent( |
||||
context, |
||||
BookDetailedActivity::class.java |
||||
) |
||||
BitIntentDataManager.getInstance().putData(intent, books) |
||||
context.startActivity(intent) |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun needGetInfo(bookBean: SearchBookBean): Boolean { |
||||
if (bookBean.author.isNullOrEmpty()) return true |
||||
if (bookBean.type.isNullOrEmpty()) return true |
||||
if (bookBean.desc.isNullOrEmpty()) return true |
||||
return if (bookBean.lastChapter.isNullOrEmpty()) true else bookBean.imgUrl.isNullOrEmpty() |
||||
} |
||||
|
||||
private fun books2SearchBookBean(bookBean: SearchBookBean, books: List<Book>) { |
||||
bookBean.sourceCount = books.size |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.author)) break |
||||
val author = book.author |
||||
if (!StringHelper.isEmpty(author)) { |
||||
bookBean.author = author |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.type)) break |
||||
val type = book.type |
||||
if (!StringHelper.isEmpty(type)) { |
||||
bookBean.type = type |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.desc)) break |
||||
val desc = book.desc |
||||
if (!StringHelper.isEmpty(desc)) { |
||||
bookBean.desc = desc |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.status)) break |
||||
val status = book.status |
||||
if (!StringHelper.isEmpty(status)) { |
||||
bookBean.status = status |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.wordCount)) break |
||||
val wordCount = book.wordCount |
||||
if (!StringHelper.isEmpty(wordCount)) { |
||||
bookBean.wordCount = wordCount |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.lastChapter)) break |
||||
val lastChapter = book.newestChapterTitle |
||||
if (!StringHelper.isEmpty(lastChapter)) { |
||||
bookBean.lastChapter = lastChapter |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.updateTime)) break |
||||
val updateTime = book.updateDate |
||||
if (!StringHelper.isEmpty(updateTime)) { |
||||
bookBean.updateTime = updateTime |
||||
break |
||||
} |
||||
} |
||||
for (book in books) { |
||||
if (!StringHelper.isEmpty(bookBean.imgUrl)) break |
||||
val imgUrl = book.imgUrl |
||||
if (!StringHelper.isEmpty(imgUrl)) { |
||||
bookBean.imgUrl = imgUrl |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
private fun searchBookBean2Book(bean: SearchBookBean, book: Book = Book()): Book { |
||||
book.name = bean.name |
||||
book.author = bean.author |
||||
book.type = bean.type |
||||
book.desc = bean.desc |
||||
book.status = bean.status |
||||
book.updateDate = bean.updateTime |
||||
book.newestChapterTitle = bean.lastChapter |
||||
book.wordCount = bean.wordCount |
||||
return book |
||||
} |
||||
|
||||
fun addAll(items: ConMVMap<SearchBookBean, Book>, searchKey: String) { |
||||
mBooks.addAll(items) |
||||
addAll(ArrayList(items.keySet()), searchKey) |
||||
} |
||||
|
||||
fun addAll(newDataS: List<SearchBookBean>, keyWord: String?) { |
||||
val copyDataS: MutableList<SearchBookBean> = ArrayList(getItems()) |
||||
val filterDataS: MutableList<SearchBookBean> = ArrayList() |
||||
when (SysManager.getSetting().searchFilter) { |
||||
0 -> filterDataS.addAll(newDataS) |
||||
1 -> for (ssb in newDataS) { |
||||
if (StringUtils.isContainEachOther(ssb.name, keyWord) || |
||||
StringUtils.isContainEachOther(ssb.author, keyWord) |
||||
) { |
||||
filterDataS.add(ssb) |
||||
} |
||||
} |
||||
2 -> for (ssb in newDataS) { |
||||
if (StringUtils.isEqual(ssb.name, keyWord) || |
||||
StringUtils.isEqual(ssb.author, keyWord) |
||||
) { |
||||
filterDataS.add(ssb) |
||||
} |
||||
} |
||||
else -> for (ssb in newDataS) { |
||||
if (StringUtils.isContainEachOther(ssb.name, keyWord) || |
||||
StringUtils.isContainEachOther(ssb.author, keyWord) |
||||
) { |
||||
filterDataS.add(ssb) |
||||
} |
||||
} |
||||
} |
||||
if (filterDataS.size > 0) { |
||||
val searchBookBeansAdd: MutableList<SearchBookBean> = java.util.ArrayList() |
||||
if (copyDataS.size == 0) { |
||||
copyDataS.addAll(filterDataS) |
||||
} else { |
||||
//存在 |
||||
for (temp in filterDataS) { |
||||
var hasSame = false |
||||
var i = 0 |
||||
val size = copyDataS.size |
||||
while (i < size) { |
||||
val searchBook = copyDataS[i] |
||||
if (TextUtils.equals(temp.name, searchBook.name) |
||||
&& TextUtils.equals(temp.author, searchBook.author) |
||||
) { |
||||
hasSame = true |
||||
break |
||||
} |
||||
i++ |
||||
} |
||||
if (!hasSame) { |
||||
searchBookBeansAdd.add(temp) |
||||
} |
||||
} |
||||
//添加 |
||||
for (temp in searchBookBeansAdd) { |
||||
if (TextUtils.equals(keyWord, temp.name)) { |
||||
for (i in copyDataS.indices) { |
||||
val searchBook = copyDataS[i] |
||||
if (!TextUtils.equals(keyWord, searchBook.name)) { |
||||
copyDataS.add(i, temp) |
||||
break |
||||
} |
||||
} |
||||
} else if (TextUtils.equals(keyWord, temp.author)) { |
||||
for (i in copyDataS.indices) { |
||||
val searchBook = copyDataS[i] |
||||
if (!TextUtils.equals(keyWord, searchBook.name) && !TextUtils.equals( |
||||
keyWord, |
||||
searchBook.author |
||||
) |
||||
) { |
||||
copyDataS.add(i, temp) |
||||
break |
||||
} |
||||
} |
||||
} else { |
||||
copyDataS.add(temp) |
||||
} |
||||
} |
||||
} |
||||
mList = copyDataS |
||||
upAdapter() |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
private fun upAdapter() { |
||||
if (System.currentTimeMillis() >= postTime + 500) { |
||||
handler.removeCallbacks(sendRunnable) |
||||
postTime = System.currentTimeMillis() |
||||
setItems(mList) |
||||
} else { |
||||
handler.removeCallbacks(sendRunnable) |
||||
handler.postDelayed(sendRunnable, 500 - System.currentTimeMillis() + postTime) |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue