6

I've recently came upon a really strange issue. I had a mapview in my layout which was inflated using databinding. After I added ViewPager2 to my layout I got this issue:

java.lang.IllegalArgumentException: Wrong state class, expecting View State but received class androidx.recyclerview.widget.RecyclerView$SavedState instead. This usually happens when two views of different type have the same id in the same hierarchy. This view's id is id/0x1. Make sure other views do not use the same id.

But it happens only when you first open first fragment and go to next one, then press back.

  • If I remove mapview from my layout everything works.
  • If I remove viewpager2 from my layout everything works.
  • If I remove databinding everything works.

It seems it has something to do with view state, but is there anything I could do to address this issue?

Here's a sample fragment code:

class TestFragment(override val layoutRes: Int = R.layout.fragment_test) : BaseFragment() {

lateinit var binding: FragmentTestBinding

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    binding = FragmentTestBinding.inflate(inflater, container, false)
    setupMap(savedInstanceState)
    return binding.root
}

override fun onResume() {
    super.onResume()
    binding.mapView.onResume()
}

override fun onPause() {
    super.onPause()
    binding.mapView.onPause()
}

private fun setupMap(savedInstanceState: Bundle?) {
    binding.mapView.onCreate(savedInstanceState)
    binding.mapView.onResume()
    binding.mapView.getMapAsync { }
}
}

Sample layout:

<?xml version="1.0" encoding="utf-8"?>
<layout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.gms.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</FrameLayout>
</layout>

To reproduce I have to open another fragment and then press back

**EDIT: ** Seems like moving viewpager in front of mapview also helps, so databinding can still be used.

SMGhost
  • 3,867
  • 6
  • 38
  • 68
  • Hi, did you find the cause of the issue? This problem is driving me nuts :S – lelloman Jan 30 '20 at 14:50
  • @lelloman, I think I ended up using older viewpager. Glad to see you found a different solution :) – SMGhost Feb 01 '20 at 15:19
  • Have exactly the same issue, have you find the solution maybe? Currently only solution i see is to go with ViewPager instead of ViewPager2... – RadoVidjen Apr 05 '20 at 15:50
  • Strange when i place viewPager2 behind the map, it works, however in cant be seen since mapView is covering VP2... – RadoVidjen Apr 05 '20 at 16:17

2 Answers2

11

The problem seems to be that MapView and ViewPager2 both inflate views and assign dynamically-generated ids to them.

ViewPager2 (version 1.0.0 atm) does that by calling ViewCompat.generateViewId(), I have no idea what MapView is doing, it's not using LayoutInflater to inflate its children views and I suspect it is not calling ViewCompat.generateViewId() either.

The cause of the issue is that the view hierarchy ends up containing 2 View with the same id (with value 1 for me), one is the RecyclerView inside the ViewPager2 (TIL that ViewPager2 is a wrapper of a RecyclerView, amazing), the other is a LinearLayout containing 2 ImageView inside the MapView.

I'm currently hacking this unfortunate situation by calling:

for(i in 0 until 1000) ViewCompat.generateViewId()

before setContentView(Int) in the Activity.

lelloman
  • 13,883
  • 5
  • 63
  • 85
2

The issue seems to be that the viewpager wraps a recyclerview to which it assigns an ID of 1, and the mapview assigns the same ID of 1. I resolved this with:

viewPager = binding.viewPager
    for (view in viewPager.children) {
        if (view is RecyclerView) {
            view.id = 99999999
            break
        }
    }
Luke
  • 91
  • 5