Обратный вызов DiffUtil приводит к исключению индекса за пределами

В настоящее время я разрабатываю список элементов с 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()
}

Любая помощь будет оценена заранее.

Никогда не используйте !! с Котлином. Никогда.

Demigod 10.07.2018 17:58

Вы можете использовать ListAdapter из библиотеки поддержки, он обрабатывает различия в фоновом потоке за вас.

LordRaydenMK 10.07.2018 20:55

@Demigod ну бывают моменты, когда это имеет смысл, но в целом нет

EpicPandaForce 11.07.2018 08:36
0
3
1 315
2

Ответы 2

DiffUtils сравнивает два непустых объекта. Итак, если newList и oldList либо пустые, вызовите notifyDataSetChanged() вместо вызова DiffUtil.

Я не знаком с используемой вами библиотекой, но я уже сталкивался с подобными проблемами раньше.

о какой библиотеке вы говорите?

Zeyad Gasser 11.07.2018 09:54

@ZeyadGasser rxjava

glagarto 11.07.2018 09:56

Я заметил ошибку:

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 - его переопределение может иметь аналогичный эффект.

Другие вопросы по теме