I am currently developing a list of items that has 4 fields that can be edited with an autocomplete feature. The underlying data structure is a list of pairs of the 4 fields and their autocomplete suggestions. Before the user edits the suggestions are empty once the user edits a request is fired and i use a diffutil to detect & dispatch the changes to the adapter. I am using the redux/mvi pattern, so i calculate the diffutil call back in the view model then pass a pair to my activity to just dispatch the changes.
Time for some code and error snippets :)
Error:
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, AutoCompleteResponse>,
private val newList: Pair, AutoCompleteResponse>) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
oldList.first!![oldItemPosition].getData().id == newList.first!![newItemPosition].getData().id
override fun getOldListSize() = oldList.first?.size!!
override fun getNewListSize() = newList.first?.size!!
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
newList.first!![newItemPosition].getData>() == oldList.first!![oldItemPosition].getData>()
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
return newList.first!![newItemPosition].getData>().second
}
}
ViewModel state reduction:
val currentEditableState = (currentState as EditableClassifyState)
val list = currentEditableState.classificationsItemInfoDiffResultPair.first
EditableClassifyState(calculateClassificationsDiffResultPair(
Observable.fromIterable(list ?: mutableListOf())
.map { itemInfo ->
val classification = when (itemInfo.getData()) {
is Pair -> itemInfo.getData>().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, result: AutoCompleteResponse = AutoCompleteResponse()): Pair, DiffUtil.DiffResult> {
return Flowable.fromArray(list)
.scan(Pair(mutableListOf(), DiffUtil.calculateDiff(ClassificationDiffCallback(Pair(mutableListOf(), AutoCompleteResponse()), Pair(mutableListOf(), AutoCompleteResponse())))))
{ state: Pair, DiffUtil.DiffResult>, next: MutableList ->
Pair(next, DiffUtil.calculateDiff(ClassificationDiffCallback(Pair(state.first, result), Pair(next, result))))
}
.skip(1)
.blockingFirst()
}
Any help would be appreciated thanks in advance.
解决方案
DiffUtils compares two objects that is not empty.
So if the newList and oldList are either empty call notifyDataSetChanged() instead of calling DiffUtil
I'm not familiar with the library you used, but I've already encounter this kind of problem before.