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.
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.
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
.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With