尽管我知道之前已经问过很多类似的问题,但我认为情况有所不同。一些答案说该错误来自Android库错误,但我使用的API 27可能已修复了该错误。
我将我的问题总结成一个最小的项目。
首先,我有一个简单的基本适配器类
abstract class EndlessListAdapter<B : Any> : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
const val VIEW_TYPE_NORMAL = 0x00
const val VIEW_TYPE_EMPTY = 0x01
const val VIEW_TYPE_FOOTER = 0x02
}
//set this property to update displayed items
var itemList: List<B> = listOf()
set(value) {
val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int = field.size
override fun getNewListSize(): Int = value.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = areItemsTheSame(field[oldItemPosition], value[newItemPosition])
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = areContentsTheSame(field[oldItemPosition], value[newItemPosition])
})
field = value
result.dispatchUpdatesTo(this)
}
//set this property to display or hide the footer
var hasMore: Boolean = false
set(value) {
if (value && !field)
notifyItemInserted(itemList.size)
else if (!value && field)
notifyItemRemoved(itemList.size)
}
override fun getItemViewType(position: Int): Int {
return when {
itemList.isEmpty() -> VIEW_TYPE_EMPTY
position == itemList.size -> VIEW_TYPE_FOOTER
else -> VIEW_TYPE_NORMAL
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_EMPTY -> object : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_empty, parent, false)) {}
VIEW_TYPE_FOOTER -> object : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_footer, parent, false)) {}
else -> object : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.common_default, parent, false)) {}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {}
override fun getItemCount(): Int {
return if (itemList.isEmpty()) 1 else if (hasMore) itemList.size + 1 else itemList.size
}
protected abstract fun areItemsTheSame(oldItem: B, newItem: B): Boolean
protected abstract fun areContentsTheSame(oldItem: B, newItem: B): Boolean
}
该活动具有三个按钮和一个
RecyclerView
。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ActivityMain">
<Button
android:id="@+id/button0"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="set 0 items" />
<Button
android:id="@+id/button4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="set 4 items" />
<Button
android:id="@+id/button8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="set 8 items" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
Activity
类是
class ActivityMain : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recycler.layoutManager = LinearLayoutManager(this)
recycler.adapter = ActivityMainAdapter()
RxView.clicks(button0).observeOn(AndroidSchedulers.mainThread()).subscribe {
(recycler.adapter as ActivityMainAdapter).let { adapter ->
adapter.itemList = listOf()
adapter.hasMore = false
}
}
RxView.clicks(button4).observeOn(AndroidSchedulers.mainThread()).subscribe {
(recycler.adapter as ActivityMainAdapter).let { adapter ->
adapter.itemList = listOf(Item(1, "Bob"), Item(2, "Mary"), Item(3, "Ken"), Item(4, "Samantha"))
adapter.hasMore = false
}
}
RxView.clicks(button8).observeOn(AndroidSchedulers.mainThread()).subscribe {
(recycler.adapter as ActivityMainAdapter).let { adapter ->
adapter.itemList = listOf(Item(1, "Jan"), Item(2, "Feb"), Item(3, "Mar"), Item(4, "Apr"), Item(5, "May"), Item(6, "Jun"), Item(7, "Jul"), Item(8, "Aug"))
adapter.hasMore = true
}
}
}
}
在活动类中,我使用了Item和ActivityMainAdapter类,它们的实现方式如下
class Item(val key: Int, val desc: String)
class ActivityMainAdapter : EndlessListAdapter<Item>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
VIEW_TYPE_NORMAL -> object : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.activity_main_viewholder, parent, false)) {}
else -> super.onCreateViewHolder(parent, viewType)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
VIEW_TYPE_NORMAL -> holder.itemView.let {
it.key.text = itemList[position].key.toString()
it.desc.text = itemList[position].desc
}
else -> super.onBindViewHolder(holder, position)
}
}
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem.key == newItem.key
override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean = oldItem.key == newItem.key && oldItem.desc == newItem.desc
}
当我运行主要活动并单击button4或button8时,出现异常:
09-05 17:08:35.787 4087-4087/demo.p1894 E/AndroidRuntime: FATAL EXCEPTION: main
Process: demo.p1894, PID: 4087
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{3c21c3e position=4 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent} android.support.v7.widget.RecyclerView{79870ac VFED..... .F....I. 0,432-1080,489 #7f080071 app:id/recycler}, adapter:demo.p1894.ActivityMainAdapter@f449175, layout:android.support.v7.widget.LinearLayoutManager@a01c50a, context:demo.p1894.ActivityMain@6be630b
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5447)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5629)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5589)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5585)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2231)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1558)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1518)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:610)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3670)
at android.support.v7.widget.RecyclerView.onMeasure(RecyclerView.java:3129)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.support.v7.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:400)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1465)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:748)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:630)
at android.view.View.measure(View.java:18830)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5954)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2643)
at android.view.View.measure(View.java:18830)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2136)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1248)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1484)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1139)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6064)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:860)
at android.view.Choreographer.doCallbacks(Choreographer.java:672)
at android.view.Choreographer.doFrame(Choreographer.java:608)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:846)
at android.os.Handler.handleCallback(Handler.java:742)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5653)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Z
答案 0 :(得分:0)
我自己解决了。这是由于空视图与DiffUtil冲突所致。
在使用DiffUtil类时,在回收者视图上具有页眉,页脚或空白视图时,这是一个常见错误。