'ViewPager2 crash
I am using Advance Navigation Component with BottomNavigationView.
In one tab I have ViewPager2. When I clicked on the tab for the first time, it worked fine.
Although the second time, come on that tab application keep crashing. Below is the crash log. How can I fix this?
java.lang.IllegalArgumentException
at androidx.core.util.Preconditions.checkArgument(Preconditions.java:36)
at androidx.viewpager2.adapter.FragmentStateAdapter.onAttachedToRecyclerView(FragmentStateAdapter.java:140)
at androidx.recyclerview.widget.RecyclerView.setAdapterInternal(RecyclerView.java:1206)
at androidx.recyclerview.widget.RecyclerView.setAdapter(RecyclerView.java:1158)
at androidx.viewpager2.widget.ViewPager2.setAdapter(ViewPager2.java:460)
at com..ui.home.history.HistoryFragment.setupAdapter(HistoryFragment.kt:25)
at com..ui.home.history.HistoryFragment.viewSetup(HistoryFragment.kt:21)
at com.****.base.BaseFragment.onViewCreated(BaseFragment.kt:37)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:332)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1187)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1434)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1497)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2625)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2577)
at androidx.fragment.app.Fragment.performActivityCreated(Fragment.java:2722)
at androidx.fragment.app.FragmentStateManager.activityCreated(FragmentStateManager.java:346)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1188)
at androidx.fragment.app.FragmentManager.addAddedFragments(FragmentManager.java:2224)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1997)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1953)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1849)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6940)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Here is my code for the fragment:
private val adapter by lazy {
HistoryPagerAdapter(this)
}
override fun viewSetup() {
binding.vpBuySell.adapter = adapter
TabLayoutMediator(
binding.tabBuySell,
binding.vpBuySell,
TabLayoutMediator.TabConfigurationStrategy { tab: TabLayout.Tab, i: Int ->
tab.text = when (i) {
0 -> getString(R.string.buy)
1 -> getString(R.string.sell)
else -> getString(R.string.buy)
}
})
}
Here is the UI code:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/blue_122e47">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="@color/blue_06233e"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:titleTextColor="@color/white">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvTitle"
style="@style/ToolbarTitleTextStyle"
android:text="@string/history" />
<TextView
android:id="@+id/btnExport"
android:layout_width="wrap_content"
android:layout_height="@dimen/_24sdp"
android:layout_gravity="end"
android:layout_marginEnd="@dimen/_8sdp"
android:fontFamily="@font/helvetica_neue_medium"
android:insetLeft="0dp"
android:gravity="center"
android:background="@drawable/shape_export_button"
android:insetTop="0dp"
android:insetRight="0dp"
android:insetBottom="0dp"
android:foreground="?selectableItemBackground"
android:paddingBottom="@dimen/_2sdp"
android:paddingStart="@dimen/_8sdp"
android:paddingEnd="@dimen/_8sdp"
android:text="@string/export"
android:textAllCaps="false"
android:textColor="@color/white"
android:textSize="@dimen/_12ssp" />
</androidx.appcompat.widget.Toolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabBuySell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/blue_122e47"
app:tabIndicatorFullWidth="false"
app:tabIndicatorGravity="bottom"
app:tabTextAppearance="@style/HistoryTabTextStyle"
app:tabTextColor="@color/gray_697b8b"
app:tabSelectedTextColor="@color/white"
app:tabIndicatorHeight="@dimen/_2sdp"
app:tabIndicatorColor="@color/blue_47cfff"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:tabGravity="start"
app:tabMode="scrollable" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vpBuySell"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabBuySell" />
</androidx.constraintlayout.widget.ConstraintLayout>
Here is my adapter code:
class HistoryPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> HistoryBuyFragment()
1 -> HistorySellFragment()
else -> HistoryBuyFragment()
}
}
}
Solution 1:[1]
The actual error is of the lazy initialisation of the adapter. I also don't know why that happened.
Solution 2:[2]
I had the same crash with my ViewPager2
implementation. In my case I was creating an adapter in onCreate
and setting it to ViewPager
in onViewCreated
. Like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = FragmentAdapter(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding?.viewPager?.adapter = adapter
}
I've fixed the crash, combining adapter creation and setting into one method - onViewCreated
. Like this:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = FragmentAdapter(this, month)
binding?.viewPager?.adapter = adapter
}
Solution 3:[3]
I ran into this issue with BottomNavigationView when I switched between fragments back and forward. The problem was that I used Kotlin's by lazy {} definition of a value and I was setting the adapter in onViewCreated. For some reason, the fragment state could not be properly restored and it was not possible to set the adapter even after I used some "hacks" from this post, like modifying some lifecycle methods, etc.
That pager adapter is automatically restored during state restoration. This is why you might not want to persist your adapter, but always set a new in onViewCreated. Then just wait for automatic restoration to trigger and have your previous state.
Fix:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Anything you set here will be overriden during restoration
viewBinding.pager.adapter = DemoCollectionPagerAdapter(this)
var mediator = TabLayoutMediator(viewBinding.tabLayout, viewBinding.pager) { tab, position ->
when (position){
0 -> tab.text = getString(R.string.global_rooms).uppercase()
1 -> tab.text = getString(R.string.global_devices).uppercase()
2 -> tab.text = getString(R.string.global_presets).uppercase()
}
}.attach()
}
Solution 4:[4]
I had the same error when I got back to the fragment, and I solved the problem after I read Oleh's comment that said:
I assume that on a retained fragment lazy property cause memory leak because of holding a reference to the old view which leads to crash
So I set the adapter to null in onPause like this code, and then the problem is disappeared:
override fun onPause() {
super.onPause()
binding?.run {
viewPager.adapter = null
}
}
Solution 5:[5]
Try out this solution:
import androidx.viewpager2.adapter.FragmentStateAdapter
class HistoryPagerAdapter(activity: AppCompatActivity, private var itemCount: Int): FragmentStateAdapter(activity) {
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> HistoryBuyFragment()
1 -> HistorySellFragment()
else -> HistoryBuyFragment()
}
}
Code for fragment:
private val TAB_ITEMS_COUNT = 2
adapter = HistoryPagerAdapter((activity as AppCompatActivity), TAB_ITEMS_COUNT)
binding.vpBuySell.adapter = adapter
TabLayoutMediator(binding.tabBuySell, binding.vpBuySell) { tab, position ->
when(position)
{
0 -> tab.text = getString(R.string.buy)
1 -> tab.text = getString(R.string.sell)
}
}.attach()
binding.tabBuySell.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener
{
override fun onTabReselected(tabItem: TabLayout.Tab?) {}
override fun onTabUnselected(tabItem: TabLayout.Tab?) {}
override fun onTabSelected(tabItem: TabLayout.Tab) {
binding.vpBuySell.currentItem = tabItem.position
}
})
Solution 6:[6]
If you are using Navigation Component, the fragment's view gets recreated when you navigate back. For some reason in my case, the adapter wasn't detaching automatically when the fragment's view was destroyed. The error was: I kept an already attached adapter reference and was trying to add it to a new pager's view. Setting adapter = null inside onDestroyView() worked in my case.
override fun onDestroyView() {
pager.adapter = null
super.onDestroyView()
}
Solution 7:[7]
When you set the viewpager2 adapter, first check viewpager already adapter, else set the adapter.
For example,
if(viewPager.adapter == null) {
viewPager.adapter = adapter
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Peter Mortensen |
Solution 2 | Peter Mortensen |
Solution 3 | Peter Mortensen |
Solution 4 | Peter Mortensen |
Solution 5 | Peter Mortensen |
Solution 6 | G_comp |
Solution 7 | Peter Mortensen |