246 lines
9.0 KiB
Kotlin
246 lines
9.0 KiB
Kotlin
package jp.juggler.subwaytooter.columnviewholder
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.os.Handler
|
|
import android.os.SystemClock
|
|
import android.view.ViewGroup
|
|
import androidx.recyclerview.widget.DiffUtil
|
|
import androidx.recyclerview.widget.ListUpdateCallback
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import jp.juggler.subwaytooter.ActMain
|
|
import jp.juggler.subwaytooter.api.entity.TimelineItem
|
|
import jp.juggler.subwaytooter.column.Column
|
|
import jp.juggler.subwaytooter.column.HeaderType
|
|
import jp.juggler.subwaytooter.column.toAdapterIndex
|
|
import jp.juggler.subwaytooter.column.toListIndex
|
|
import jp.juggler.subwaytooter.itemviewholder.ItemViewHolder
|
|
import jp.juggler.subwaytooter.itemviewholder.bind
|
|
import jp.juggler.util.log.LogCategory
|
|
import jp.juggler.util.ui.AdapterChange
|
|
import jp.juggler.util.ui.AdapterChangeType
|
|
|
|
class ItemListAdapter(
|
|
private val activity: ActMain,
|
|
private val column: Column,
|
|
internal val columnVh: ColumnViewHolder,
|
|
private val bSimpleList: Boolean,
|
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|
|
|
companion object {
|
|
private val log = LogCategory("ItemListAdapter")
|
|
}
|
|
|
|
private class DiffCallback(
|
|
val oldList: List<TimelineItem>,
|
|
val newList: List<TimelineItem>,
|
|
val biasListIndex: Int,
|
|
) : DiffUtil.Callback() {
|
|
|
|
override fun getOldListSize(): Int {
|
|
return oldList.size - biasListIndex
|
|
}
|
|
|
|
override fun getNewListSize(): Int {
|
|
return newList.size - biasListIndex
|
|
}
|
|
|
|
override fun areItemsTheSame(oldAdapterIndex: Int, newAdapterIndex: Int): Boolean {
|
|
val oldListIndex = oldAdapterIndex + biasListIndex
|
|
val newListIndex = newAdapterIndex + biasListIndex
|
|
|
|
// header?
|
|
if (oldListIndex < 0 || newListIndex < 0) return oldListIndex == newListIndex
|
|
|
|
// compare object address
|
|
return oldList[oldListIndex] === newList[newListIndex]
|
|
}
|
|
|
|
override fun areContentsTheSame(oldAdapterIndex: Int, newAdapterIndex: Int): Boolean {
|
|
val oldListIndex = oldAdapterIndex + biasListIndex
|
|
val newListIndex = newAdapterIndex + biasListIndex
|
|
|
|
// headerは毎回更新する
|
|
return !(oldListIndex < 0 || newListIndex < 0)
|
|
}
|
|
}
|
|
|
|
private var list: ArrayList<TimelineItem>
|
|
private val handler: Handler
|
|
|
|
init {
|
|
this.list = ArrayList()
|
|
this.handler = activity.handler
|
|
setHasStableIds(true)
|
|
}
|
|
|
|
override fun getItemCount(): Int {
|
|
return column.toAdapterIndex(column.listData.size)
|
|
}
|
|
|
|
override fun getItemId(position: Int): Long {
|
|
return try {
|
|
list[column.toListIndex(position)].listViewItemId
|
|
} catch (ignored: Throwable) {
|
|
0L
|
|
}
|
|
}
|
|
|
|
override fun getItemViewType(position: Int): Int {
|
|
val headerType = column.type.headerType
|
|
if (headerType == null || position > 0) return 0
|
|
return headerType.viewType
|
|
}
|
|
|
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
|
activity
|
|
when (viewType) {
|
|
0 -> {
|
|
val holder = ItemViewHolder(activity)
|
|
holder.viewRoot.tag = holder
|
|
return ViewHolderItem(holder)
|
|
}
|
|
|
|
HeaderType.Profile.viewType -> {
|
|
return ViewHolderHeaderProfile(activity, parent)
|
|
}
|
|
|
|
HeaderType.Search.viewType -> {
|
|
return ViewHolderHeaderSearch(activity, parent)
|
|
}
|
|
|
|
HeaderType.Instance.viewType -> {
|
|
return ViewHolderHeaderInstance(activity, parent)
|
|
}
|
|
|
|
HeaderType.Filter.viewType -> {
|
|
return ViewHolderHeaderFilter(activity, parent)
|
|
}
|
|
HeaderType.ProfileDirectory.viewType -> {
|
|
return ViewHolderHeaderProfileDirectory(activity, parent)
|
|
}
|
|
else -> error("unknown viewType: $viewType")
|
|
}
|
|
}
|
|
|
|
fun findHeaderViewHolder(listView: RecyclerView): ViewHolderHeaderBase? {
|
|
return when (column.type.headerType) {
|
|
null -> null
|
|
else -> listView.findViewHolderForAdapterPosition(0) as? ViewHolderHeaderBase
|
|
}
|
|
}
|
|
|
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, adapterIndex: Int) {
|
|
if (holder is ViewHolderItem) {
|
|
val listIndex = column.toListIndex(adapterIndex)
|
|
holder.ivh.bind(this, column, bSimpleList, list[listIndex])
|
|
} else if (holder is ViewHolderHeaderBase) {
|
|
holder.bindData(column)
|
|
}
|
|
}
|
|
|
|
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
|
|
if (holder is ViewHolderItem) {
|
|
holder.ivh.onViewRecycled()
|
|
} else if (holder is ViewHolderHeaderBase) {
|
|
holder.onViewRecycled()
|
|
}
|
|
}
|
|
|
|
@SuppressLint("NotifyDataSetChanged")
|
|
fun notifyChange(
|
|
reason: String,
|
|
changeList: List<AdapterChange>? = null,
|
|
reset: Boolean = false,
|
|
) {
|
|
|
|
val timeStart = SystemClock.elapsedRealtime()
|
|
|
|
// カラムから最新データをコピーする
|
|
val newList = ArrayList<TimelineItem>()
|
|
newList.ensureCapacity(column.listData.size)
|
|
newList.addAll(column.listData)
|
|
|
|
when {
|
|
// 変更リストが指定された場合はヘッダ部分と変更部分を通知する
|
|
changeList != null -> {
|
|
|
|
log.d("notifyChange: changeList=${changeList.size},reason=$reason")
|
|
|
|
this.list = newList
|
|
|
|
// ヘッダは毎回更新する
|
|
// (ヘッダだけ更新するためにカラのchangeListが渡される)
|
|
if (column.type.headerType != null) {
|
|
notifyItemRangeChanged(0, 1)
|
|
}
|
|
|
|
// 変更リストを順番に通知する
|
|
for (c in changeList) {
|
|
val adapterIndex = column.toAdapterIndex(c.listIndex)
|
|
log.d("notifyChange: ChangeType=${c.type} offset=$adapterIndex,count=${c.count}")
|
|
when (c.type) {
|
|
AdapterChangeType.RangeInsert -> notifyItemRangeInserted(
|
|
adapterIndex,
|
|
c.count
|
|
)
|
|
AdapterChangeType.RangeRemove -> notifyItemRangeRemoved(
|
|
adapterIndex,
|
|
c.count
|
|
)
|
|
AdapterChangeType.RangeChange -> notifyItemRangeChanged(
|
|
adapterIndex,
|
|
c.count
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
reset -> {
|
|
log.d("notifyChange: DataSetChanged! reason=$reason")
|
|
this.list = newList
|
|
notifyDataSetChanged()
|
|
}
|
|
|
|
else -> {
|
|
|
|
val diffResult = DiffUtil.calculateDiff(
|
|
DiffCallback(
|
|
oldList = this.list, // 比較対象の古いデータ
|
|
newList = newList,
|
|
biasListIndex = column.toListIndex(0)
|
|
),
|
|
false // ログを見た感じ、移動なんてなかった
|
|
)
|
|
val time = SystemClock.elapsedRealtime() - timeStart
|
|
log.d("notifyChange: size=${newList.size},time=${time}ms,reason=$reason")
|
|
|
|
this.list = newList
|
|
diffResult.dispatchUpdatesTo(object : ListUpdateCallback {
|
|
|
|
override fun onInserted(position: Int, count: Int) {
|
|
log.d("notifyChange: notifyItemRangeInserted offset=$position,count=$count")
|
|
notifyItemRangeInserted(position, count)
|
|
}
|
|
|
|
override fun onRemoved(position: Int, count: Int) {
|
|
log.d("notifyChange: notifyItemRangeRemoved offset=$position,count=$count")
|
|
notifyItemRangeRemoved(position, count)
|
|
}
|
|
|
|
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
|
log.d("notifyChange: notifyItemRangeChanged offset=$position,count=$count")
|
|
notifyItemRangeChanged(position, count, payload)
|
|
}
|
|
|
|
override fun onMoved(fromPosition: Int, toPosition: Int) {
|
|
log.d("notifyChange: notifyItemMoved from=$fromPosition,to=$toPosition")
|
|
notifyItemMoved(fromPosition, toPosition)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// diffを取る部分をワーカースレッドで実行したいが、直後にスクロール位置の処理があるので差支えがある…
|
|
}
|
|
}
|