【问题标题】:How to fix 'Design assumption violated' error in ViewPager2?如何修复 ViewPager2 中的“违反设计假设”错误?
【发布时间】:2019-11-22 19:51:00
【问题描述】:

我正在使用 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$4.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

【问题讨论】:

  • 我看到将适配器列表设置为新列表会自动将寻呼机移动到第一项。所以在调用 setCurrentItem 时确实没有什么可以滚动的。
  • @glucaio 当新列表的项比前一个列表多时,索引不设置为0。
  • 你是对的。我也看到了。可能会提交错误?设置当前项目似乎仍在进行中。
  • @Amir 您可以将您的代码示例项目共享为 git repo 吗?所以我们可以尽力帮助您

标签: android kotlin android-viewpager2


【解决方案1】:

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

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

在我的例子中,我在 FragmentStateAdapter 类中重写了 getItemId() 方法。当我摆脱我的getItemId() 方法后,“IllegalStateException:违反设计假设”错误就消失了! :-)

【讨论】:

  • 对于遇到此问题但无法删除其自定义实现的getItemId() 的任何人,您还应该覆盖containsItem(long)。这在 Javadocs 中有说明
  • @PatrickGrayson 的评论是这个问题的真正答案
【解决方案2】:

我正在使用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。

如果你打开getItemId()的实现请注意这部分:

  • @return 稳定项目 ID {@link RecyclerView.Adapter#hasStableIds()}

您需要为每个项目提供唯一的 ID。

【讨论】:

  • 我确实实现了 getItemId(),这是必须的吗?我使用的是材料 1.3.0
  • 如果您在每个数据中都有唯一的 id,或者您可以确保您的数据永远不会重复,那么您不必这样做
【解决方案3】:

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

ViewPager2 1.0.0-beta01 Release Notes中查看更多信息

【讨论】:

  • 我正在使用androidx.viewpager2.widget.ViewPager2,在com.google.android.material:material:1.1.0-alpha08中可用
  • 它与 com.google.android.material:material:1.1.0-alpha08 依赖于 androidx.viewpager2:viewpager2:1.0.0-alpha06 是同一个 ViewPager2 库。您必须等待,直到 Material Components 库将更新为使用 androidx.viewpager2:viewpager2:1.0.0-beta01,或者您可以强制使用当前 ViewPager2 库版本和 implementation("androidx.viewpager2:viewpager2:1.0.0-beta02")
【解决方案4】:

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

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)
}

【讨论】:

    【解决方案5】:

    我的问题是我正在自定义 containsItem() 以具有一些条件,在这些条件下我从 ViewPager 中删除 Fragments,然后返回 false

    旋转手机后遇到Design assumption violated(然后刷ViewPager2)。

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

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

    更新:

    containsItem 方法没有“CallSuper”注解,因此不需要调用

    这样就对了,不用调用super;在返回布尔值之前我没有明确调用它;只是返回它来解决这个问题

    【讨论】:

    • containsItem 方法没有“CallSuper”注解,因此不需要调用
    • @DennisVA 仅供参考,您不必调用它.. 但您必须返回一个布尔值,并且每当您不想显式返回 true/false 时,您都会返回晚饭类方法..每当我返回显式值时都会引发我的问题..所以我通过返回超类方法来解决它!
    • 如果你只是调用了超类,那和删除这段代码是一样的吧?
    • @chitgoks 我没听懂你......你会调用超类并返回什么?
    • @zain 我的意思是,如果你只调用 return super.containsItem(itemId);那么它与删除该方法相同。所以没有必要放那个。
    猜你喜欢
    • 2020-05-02
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 2019-09-05
    • 2019-09-21
    • 1970-01-01
    • 2021-06-02
    • 1970-01-01
    相关资源
    最近更新 更多