如何修复 ViewPager2 中的 'Design assumption violated' 错误?

How to fix 'Design assumption violated' error in ViewPager2?

我正在使用 ViewPager2、Kotlin 和 AndroidX。 当适配器不在索引 0 并且我更改适配器列表并将当前项设置为索引 0 时,将引发异常。

java.lang.IllegalStateException: Design assumption violated.
        at androidx.viewpager2.widget.ViewPager2.updateCurrentItem(ViewPager2.java:538)
        at androidx.viewpager2.widget.ViewPager2.onAnimationsFinished(ViewPager2.java:518)
        at androidx.recyclerview.widget.RecyclerView$ItemAnimator.isRunning(RecyclerView.java:13244)
        at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:515)
        at android.view.View.layout(View.java:15596)

在ViewPager2的第537行有一个if导致了异常:

        // Extra checks verifying assumptions
        // TODO: remove after testing
        View snapView1 = mPagerSnapHelper.findSnapView(mLayoutManager);
        View snapView2 = mLayoutManager.findViewByPosition(snapPosition);
        if (snapView1 != snapView2) {
            throw new IllegalStateException("Design assumption violated.");
        }

这是我更新适配器列表的方式:

adapter.list = newList
adapter.notifyDataSetChanged()
viewpager.setCurrentItem(0, true)

只有当smoothScroll设置为true

时才会发生

我错过了什么?

编辑: 我正在使用 androidx.viewpager2.widget.ViewPager2 com.google.android.material:material:1.1.0-alpha08

我遇到了同样的问题,但该错误似乎已在当前版本中修复(至少对我而言)androidx.viewpager2:viewpager2:1.0.0-beta01

ViewPager2 1.0.0-beta01 Release Notes

中查看更多内容

我在 ViewPager2 配置更改时遇到了同样的问题。我正在使用:

implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta03'

在我的例子中,我覆盖了 FragmentStateAdapter class 中的 getItemId() 方法。当我摆脱我的 getItemId() 方法时,“IllegalStateException:违反设计假设”错误不再存在! :-)

我正在使用 androidx.viewpager2:viewpager2:1.0.0

对于那些仍然坚持这一点的人,需要实施 getItemId() containsItem()。请检查您的 getItemId()containsItem() 实施。我的问题是当我提供 10 件商品时 可能有几件商品具有相同的值 ,出现此错误。

媒体有多个具有相同值的数据。

private var mediaId = media.map { it.hashCode().toLong() }

override fun getItemId(position: Int): Long = mediaId[position]

override fun containsItem(itemId: Long): Boolean = mediaId.contains(itemId)

当您更新数据甚至只是滑动时,viewpager 会很混乱,因为您使用非唯一 ID 实现了 getItemId()。解决方案只是 确保您的数据是唯一的或拥有自己的 ID。 *我的媒体数据没有 ID。

If you open the implementation of getItemId() notes this part :

  • @return stable item id {@link RecyclerView.Adapter#hasStableIds()}

you need to have unique id for each of item.

我的问题是我正在自定义 containsItem() 以在某些条件下从 ViewPager 中删除片段,然后 return false;

旋转phone(然后滑动ViewPager2)后遇到Design assumption violated

我通过 return 超级 class 方法解决了这个问题:return super.containsItem(itemId);

@Override
public boolean containsItem(long itemId) {
    // Your code
    return super.containsItem(itemId);
}

更新:

The containsItem method does not have the "CallSuper" annotation and is therefore not required to be called

没错,不用调用super;在 returning 一个布尔值之前我没有明确地调用它;只是 return 解决这个问题

这是一个简单用例的解决方案

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter

class ViewPager2Adapter(
val dataList: List<Item> = listOf(),
fragmentActivity: FragmentActivity
) :
FragmentStateAdapter(fragmentActivity) {

val pageIds= dataList.map { it.hashCode().toLong() }.toMutableList()

override fun createFragment(position: Int): Fragment {
    return MyFragment.newInstance(dataList[position])
}

override fun getItemCount(): Int {
    return dataList.size
}

override fun getItemId(position: Int): Long {
    return pageIds[position]
}

override fun containsItem(itemId: Long): Boolean {
    return pageIds.contains(itemId)
}
}

然后我遇到了一个特定的用例,当我用一个新的对象替换该位置的对象时,我得到了异常。

解法: 现在,如果您正在对适配器进行数据更改,即用新对象替换 dataList 中的对象,请确保将旧对象的 pageIds 中的 id 替换为新对象。

(viewPager2.adapter as? ViewPager2Adapter)?.apply {
pageIds[oldIndex] = (NewItemObject).hashCode().toLong()
notifyItemChanged(oldIndex)
}