SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/ItemListAdapter.kt

233 lines
7.2 KiB
Kotlin
Raw Normal View History

package jp.juggler.subwaytooter
import android.os.Handler
import android.os.SystemClock
import android.support.v7.util.DiffUtil
import android.support.v7.util.ListUpdateCallback
import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
import jp.juggler.subwaytooter.api.entity.TimelineItem
import jp.juggler.subwaytooter.util.LogCategory
internal 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 inner 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.list_data.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.headerType
if(headerType == null || position > 0) return 0
return headerType.viewType
}
override fun onCreateViewHolder(parent : ViewGroup?, viewType : Int) : RecyclerView.ViewHolder {
when(viewType) {
0 -> {
val holder = ItemViewHolder(activity)
holder.viewRoot.tag = holder
return ViewHolderItem(holder)
}
Column.HeaderType.Profile.viewType -> {
val viewRoot =
activity.layoutInflater.inflate(R.layout.lv_header_profile, parent, false)
val holder = ViewHolderHeaderProfile(activity, viewRoot)
viewRoot.tag = holder
return holder
}
Column.HeaderType.Search.viewType -> {
val viewRoot =
activity.layoutInflater.inflate(R.layout.lv_header_search_desc, parent, false)
val holder = ViewHolderHeaderSearch(activity, viewRoot)
viewRoot.tag = holder
return holder
}
Column.HeaderType.Instance.viewType -> {
val viewRoot =
activity.layoutInflater.inflate(R.layout.lv_header_instance, parent, false)
val holder = ViewHolderHeaderInstance(activity, viewRoot)
viewRoot.tag = holder
return holder
}
else -> throw RuntimeException("unknown viewType: $viewType")
}
}
fun findHeaderViewHolder(listView : RecyclerView) : ViewHolderHeaderBase? {
return when(column.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()
}
}
fun notifyChange(
reason : String,
changeList : List<AdapterChange>? = null,
reset : Boolean = false
) {
val time_start = SystemClock.elapsedRealtime()
// カラムから最新データをコピーする
val new_list = ArrayList<TimelineItem>()
new_list.ensureCapacity(column.list_data.size)
new_list.addAll(column.list_data)
when {
// 変更リストが指定された場合はヘッダ部分と変更部分を通知する
changeList != null -> {
log.d("notifyChange: changeList=${changeList.size},reason=$reason")
this.list = new_list
// ヘッダは毎回更新する
// (ヘッダだけ更新するためにカラのchangeListが渡される)
if(column.headerType != null){
notifyItemRangeChanged(0, 1)
}
// 変更リストを順番に通知する
for(c in changeList) {
val adapterIndex = column.toAdapterIndex(c.listIndex)
log.d("notifyChange: ChangeType=${c.type} pos=$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 = new_list
notifyDataSetChanged()
}
else -> {
val diffResult = DiffUtil.calculateDiff(
DiffCallback(
oldList = this.list, // 比較対象の古いデータ
newList = new_list,
biasListIndex = column.toListIndex(0)
),
false // ログを見た感じ、移動なんてなかった
)
val time = SystemClock.elapsedRealtime() - time_start
log.d("notifyChange: size=${new_list.size},time=${time}ms,reason=$reason")
this.list = new_list
diffResult.dispatchUpdatesTo(object : ListUpdateCallback {
override fun onInserted(position : Int, count : Int) {
log.d("notifyChange: notifyItemRangeInserted pos=$position,count=$count")
notifyItemRangeInserted(position, count)
}
override fun onRemoved(position : Int, count : Int) {
log.d("notifyChange: notifyItemRangeRemoved pos=$position,count=$count")
notifyItemRangeRemoved(position, count)
}
override fun onChanged(position : Int, count : Int, payload : Any?) {
log.d("notifyChange: notifyItemRangeChanged pos=$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を取る部分をワーカースレッドで実行したいが、直後にスクロール位置の処理があるので差支えがある…
}
}