В настоящее время я разрабатываю список элементов с 4 полями, которые можно редактировать с помощью функции автозаполнения. Базовая структура данных представляет собой список пар из 4 полей и их варианты автозаполнения. Перед тем, как пользователь редактирует, предложения пусты, как только пользователь редактирует запрос, и я использую diffutil для обнаружения и отправки изменений в адаптер. Я использую шаблон redux / mvi, поэтому я вычисляю обратный вызов diffutil в модели представления, а затем передаю пару своей активности, чтобы просто отправить изменения. Время для кода и фрагментов ошибок :)
Ошибка:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{88b1b52 position=5 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent} android.support.v7.widget.RecyclerView{c60ba82 VFED..... ......I. 11,11-1069,990 #7f0900ba app:id/rv_suggestions}, adapter:com.accenture.shiny.screens.classify.ClassifyActivity$setupRecyclerView$1@8f3593, layout:android.support.v7.widget.LinearLayoutManager@1fec9d0, context:com.accenture.shiny.screens.classify.ClassifyActivity@9caa09a
DiffUtil:
class ClassificationDiffCallback(private val oldList: Pair<MutableList<ItemInfo>, AutoCompleteResponse>,
private val newList: Pair<MutableList<ItemInfo>, AutoCompleteResponse>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList.first!![oldItemPosition].getData<Classification>().id == newList.first!![newItemPosition].getData<Classification>().id
override fun getOldListSize() = oldList.first?.size!!
override fun getNewListSize() = newList.first?.size!!
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
newList.first!![newItemPosition].getData<Pair<Classification, AutoCompleteResponse>>() == oldList.first!![oldItemPosition].getData<Pair<Classification, AutoCompleteResponse>>()
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
return newList.first!![newItemPosition].getData<Pair<Classification, AutoCompleteResponse>>().second
}
}
Уменьшение состояния ViewModel:
val currentEditableState = (currentState as EditableClassifyState)
val list = currentEditableState.classificationsItemInfoDiffResultPair.first
EditableClassifyState(calculateClassificationsDiffResultPair(
Observable.fromIterable(list ?: mutableListOf())
.map { itemInfo ->
val classification = when (itemInfo.getData<Any>()) {
is Pair<*, *> -> itemInfo.getData<Pair<Any, DiffUtil.DiffResult>>().first
else -> Unit
}
ItemInfo(Pair(classification, result),
if (classification === Unit) R.layout.manual_classification else R.layout.classification_item_layout)
.setId((classification as? Classification)?.id
?: 0)
}.toList().blockingGet(), result))
fun calculateClassificationsDiffResultPair(list: MutableList<ItemInfo>, result: AutoCompleteResponse = AutoCompleteResponse()): Pair<MutableList<ItemInfo>, DiffUtil.DiffResult> {
return Flowable.fromArray(list)
.scan(Pair(mutableListOf(), DiffUtil.calculateDiff(ClassificationDiffCallback(Pair(mutableListOf(), AutoCompleteResponse()), Pair(mutableListOf(), AutoCompleteResponse())))))
{ state: Pair<MutableList<ItemInfo>, DiffUtil.DiffResult>, next: MutableList<ItemInfo> ->
Pair(next, DiffUtil.calculateDiff(ClassificationDiffCallback(Pair(state.first, result), Pair(next, result))))
}
.skip(1)
.blockingFirst()
}
Любая помощь будет оценена заранее.
Вы можете использовать ListAdapter из библиотеки поддержки, он обрабатывает различия в фоновом потоке за вас.
@Demigod ну бывают моменты, когда это имеет смысл, но в целом нет
DiffUtils сравнивает два непустых объекта.
Итак, если newList и oldList либо пустые, вызовите notifyDataSetChanged() вместо вызова DiffUtil.
Я не знаком с используемой вами библиотекой, но я уже сталкивался с подобными проблемами раньше.
о какой библиотеке вы говорите?
@ZeyadGasser rxjava
Я заметил ошибку:
adapter.items = newItems
val result = DiffUtil.calculateDiff(
AllModulesDiffUtilsCallback(oldItems, newItems)
)
result.dispatchUpdatesTo(this)
и переопределить Adapter#setItems():
override fun setItems(items: List<RecyclerItem>) {
this.items = when (state) {
State.COLLAPSED -> items.take(4)
State.EXPANDED -> items
}
}
Произошло исключение, потому что после такой операции количество adapter#items отличается от количества newItems для COLLAPSED. Однако это должны быть одни и те же данные.
Кроме того, остерегайтесь override fun getItemCount(): Int - его переопределение может иметь аналогичный эффект.
Никогда не используйте !! с Котлином. Никогда.