DiffUtil回调导致索引超出范围异常

时间:2018-07-10 15:04:55

标签: android android-recyclerview kotlin rx-java2

我目前正在开发具有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()
}

感谢您的任何帮助。

2 个答案:

答案 0 :(得分:0)

DiffUtils比较两个不为空的对象。 因此,如果newListoldList是空呼叫notifyDataSetChanged()而不是呼叫DiffUtil

我对您使用的库不熟悉,但是以前我已经遇到过此类问题。

答案 1 :(得分:0)

我观察到以下错误:

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的{​​{1}}不同。它们应该是相同的数据。

除此之外,请注意COLLAPSED-覆盖它可能会产生相似的效果。