23

I'm using ViewPager2, Kotlin and AndroidX. When the adapter is not at index 0 and I change the adapter list and set current item to index 0 the exception is thrown.

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)

In line 537 of ViewPager2 there is an if which causes the exception:

        // 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.");
        }

This is how I'm updating the adapter list:

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

It happens only when smoothScroll is set to true

What am I missing?

Edit : I'm using androidx.viewpager2.widget.ViewPager2 available in com.google.android.material:material:1.1.0-alpha08

Amir
  • 1,628
  • 20
  • 28
  • I'm seeing that setting the adapter's list to a new list automatically moves the pager to the first item.. so there really is nothing to scroll by the time `setCurrentItem` is called. –  Jul 13 '19 at 08:10
  • @glucaio when the new list has more items than the previous list, the index is not set to 0. – Amir Jul 13 '19 at 08:18
  • You're right. I'm seeing that as well. File a bug maybe? Setting the current item definitely seems to be a work in progress still. –  Jul 13 '19 at 08:34
  • @Amir can you share the sample project of your code as git repo? so we can try to help you – AskNilesh Jul 18 '19 at 04:22

5 Answers5

25

I had this same problem with ViewPager2 on configuration change. I'm using:

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

In my case, I had overridden the getItemId() method in my FragmentStateAdapter class. When I got rid of my getItemId() method, the "IllegalStateException: Design assumption violated" error was no more! :-)

ban-geoengineering
  • 18,324
  • 27
  • 171
  • 253
  • 42
    For anyone who encounters this issue but cannot remove their custom implementation of `getItemId()`, you should also override `containsItem(long)`. This is indicated in the Javadocs – Patrick Grayson Oct 03 '19 at 13:27
  • 3
    @PatrickGrayson's comment is the real answer for this problem – Paulo Cesar Jul 02 '20 at 10:41
18

I'm using androidx.viewpager2:viewpager2:1.0.0

For whoever still stuck at this, and need to implement getItemId() containsItem(). Please chek your getItemId() and containsItem() implementation. My problem is when I serve 10 item with possibility several item have same value, this error occurs.

Media have several data with same value.

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)

while you updating the data or even simply swiping, the viewpager is confuse because you implement getItemId() with non unique id. The solution is just make sure your data is unique or have their own id. *My media datas dont have 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.

Yehezkiel L
  • 389
  • 2
  • 10
0

I had the same problem but the bug seems to be fixed (at least for me) in the current version androidx.viewpager2:viewpager2:1.0.0-beta01.

See more in ViewPager2 1.0.0-beta01 Release Notes

xsveda
  • 17,074
  • 2
  • 16
  • 16
  • I'm using `androidx.viewpager2.widget.ViewPager2` available in `com.google.android.material:material:1.1.0-alpha08` – Amir Jul 20 '19 at 11:07
  • 2
    It is the same ViewPager2 library as `com.google.android.material:material:1.1.0-alpha08` depends on `androidx.viewpager2:viewpager2:1.0.0-alpha06`. You have to wait, until Material Components library will be updated to use `androidx.viewpager2:viewpager2:1.0.0-beta01`, or you can force to use current ViewPager2 library version with `implementation("androidx.viewpager2:viewpager2:1.0.0-beta02")` – xsveda Jul 21 '19 at 12:49
0

Here is the solution for a straightforward use case

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

Then I encountered a specific use case when I was replacing my object at the position with a new one then I got the exception.

Solution: Now in case you are making data changes to the adapter i.e. with replacing an object in dataList with a new one, then make sure to replace id in pageIds of the old object with the new one.

(viewPager2.adapter as? ViewPager2Adapter)?.apply {
pageIds[oldIndex] = (NewItemObject).hashCode().toLong()
notifyItemChanged(oldIndex)
}
Muhammad Maqsood
  • 1,622
  • 19
  • 25
-1

My issue was that I was customizing containsItem() to have some conditions under which I remove Fragments from the ViewPager, and then return false;

I encounter Design assumption violated after rotating the phone (and then swipe the ViewPager2).

I solved that by just returning the super class method: return super.containsItem(itemId);

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

UPDATE:

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

This is right, no need to call the super; and I didn't call it explicitly before returning a boolean; just returning it solving this issue

Zain
  • 37,492
  • 7
  • 60
  • 84
  • The containsItem method does not have the "CallSuper" annotation and is therefore not required to be called – DennisVA Jan 27 '21 at 09:00
  • @DennisVA FYI you don't have to call it.. but you *must* return a boolean, and whenever you don't want to explicitly return true/false you return the supper class method.. my issue raised whenever I returning an explicit value.. so I solved it by returning the super class method instead! – Zain Jan 27 '21 at 09:12
  • if you just called the super class then it would be the same as removing this block of code right? – chitgoks Oct 21 '21 at 05:58
  • @chitgoks I didn't get you.. you would call the super class and return what? – Zain Oct 21 '21 at 23:14
  • @zain i meant that if you only called return super.containsItem(itemId); then it is the same as removing that method. so there is no need to put that. – chitgoks Oct 22 '21 at 01:47
  • @chitgoks I had to override that method.. If you noticed I just omitted my code withe the comment *//Your code*... When i returned false the problem appears.. But by returning the super class the problem solved. – Zain Oct 22 '21 at 03:56
  • ok. ViewPager2 is a pain. I wasted time migrating it to this because i figured they would remove ViewPager anytime sooner than later. But the animation is so slow, it does not look good when used with tabs. The actionbar menuitem changes only when the animation is done. regarding the issue in this post, containsItem() didnt work out. getItemId() alone, worked ok. – chitgoks Oct 22 '21 at 06:24