Reputation: 1866
I have a ViewPager
and a TabLayout
containing 3 Fragments. At times when I swipe through(with the help of ViewPager
) the 3 Fragments, an IllegalArgumentException
is thrown with a message that Fragment is already added: ...(Fragment at index 0 to be specific). What could be the problem as the error log is only showing something to do with FragmentManager
class and none of my Fragment classes? Below is the adapter(s) I am using
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentPagerAdapter
import androidx.fragment.app.FragmentStatePagerAdapter
data class TitledFragment(val fragment: Fragment, val title: CharSequence?)
/**
* Should be used when displaying more than 2 [Fragment]s in a view-pager otherwise use
* [FragmentViewPagerAdapter]
*/
@Suppress("KDocUnresolvedReference")
class FragmentStateViewPagerAdapter(
private val titledFragmentList: List<TitledFragment>,
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabCount = titledFragmentList.size
/**
* Selects the middle or first [Fragment] as the default selected [Fragment] from a list
* ([titledFragmentList]) of odd-numbered [Fragment]s and even-numbered [Fragment]s respectively.
* For example, a view-pager supposed to display 3 fragments ([titledFragmentList] = 3) will have
* it's default selected fragment position equal to 1(2nd [Fragment]) from the list whereas a
* view-pager supposed to display 2 fragments ([titledFragmentList] = 2) will have it's default
* selected fragment position equal to 0(1st [Fragment])
*/
val middleFragmentPosition: Int
get() = if (tabCount <= 0 || tabCount == 1) {
0
} else {
val fl = tabCount.toFloat() / 2
when {
tabCount % 2 == 0 -> fl.toInt() - 1
else -> fl.toInt()
}
}
override fun getPageTitle(position: Int): CharSequence? =
titledFragmentList[position].title ?: super.getPageTitle(position)
override fun getItem(position: Int): Fragment = titledFragmentList[position].fragment
override fun getCount(): Int = tabCount
}
/**
* Should be used when displaying less than 3 [Fragment]s in a view-pager otherwise use
* [FragmentStateViewPagerAdapter]
*/
@Suppress("KDocUnresolvedReference")
class FragmentViewPagerAdapter(
private val titledFragmentList: List<TitledFragment>,
fragmentManager: FragmentManager
) : FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabCount = titledFragmentList.size
/**
* Selects the middle or first [Fragment] as the default selected [Fragment] from a list
* ([titledFragmentList]) of odd-numbered [Fragment]s and even-numbered [Fragment]s respectively.
* For example, a view-pager supposed to display 3 fragments ([titledFragmentList] = 3) will have
* it's default selected fragment position equal to 1(2nd [Fragment]) from the list whereas a
* view-pager supposed to display 2 fragments ([titledFragmentList] = 2) will have it's default
* selected fragment position equal to 0(1st [Fragment])
*/
val middleFragmentPosition: Int
get() = if (tabCount <= 0 || tabCount == 1) {
0
} else {
val fl = tabCount.toFloat() / 2
when {
tabCount % 2 == 0 -> fl.toInt() - 1
else -> fl.toInt()
}
}
override fun getPageTitle(position: Int): CharSequence? =
titledFragmentList[position].title ?: super.getPageTitle(position)
override fun getItem(position: Int): Fragment = titledFragmentList[position].fragment
override fun getCount(): Int = tabCount
}
Upvotes: 0
Views: 443
Reputation: 28875
I think, you shouldn't hold references to fragments outside of adapter. They should be created inside the adapter. Probably the adapter can be written this way:
class FragmentStateViewPagerAdapter(
private val tabTitles: List<String>,
fragmentManager: FragmentManager
) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val tabs: List<Fragment> = (tabTitles.indices).map { YourFragment.newInstance(it) }
override fun getCount(): Int = tabTitles.size
override fun getItem(position: Int): Fragment = tabs[position]
override fun getPageTitle(position: Int): CharSequence {
// Generate a title depending on the position.
return tabTitles[position]
}
}
But your example also works. Maybe more code is required. Also strangely BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
is not required in my case (it shows error, but must not).
class MainActivity : AppCompatActivity() {
private lateinit var adapter: FragmentViewPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val titledFragments = (1..3).map { TitledFragment(PlusOneFragment(), it.toString()) }
adapter = FragmentViewPagerAdapter(titledFragments, supportFragmentManager)
with(view_pager) {
adapter = [email protected]
}
}
}
Upvotes: 1