I'm using ViewPager2
with TabLayout
and FragmentStateAdapter
to display Fragments in tabs. I found out that when I run it on slower devices (ie. Nexus 5X) or when I put a breakpoint to createFragment()
method, after I change to some later tab with Fragment, that is not created yet, Fragment on last position is shown.
In example I have 20 items. I select tab on position 15. Fragments on positions 15, 14, 16, 17, 18, 19 and 20 are created (createFragment()
is called). I would expect to create only 14, 15, 16 to be created. On slow devices or when stopping debugger inside createFragment()
method, Fragment on position 20 is shown.
This issue is not happening on the devices that are not slowed down.
MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initViewPager2WithFragments()
}
private fun initViewPager2WithFragments() {
val viewPager2: ViewPager2 = findViewById(R.id.viewpager)
val adapter = ExampleStateAdapter(supportFragmentManager, lifecycle)
viewPager2.adapter = adapter
val tablayout: TabLayout = findViewById(R.id.tablayout)
val names: Array<String> = arrayOf(
"Fragment A",
"Fragment B",
"Fragment C",
"Fragment D",
"Fragment E",
"Fragment F",
"Fragment G",
"Fragment H",
"Fragment I",
"Fragment J",
"Fragment K",
"Fragment L",
"Fragment M",
"Fragment N"
)
TabLayoutMediator(tablayout, viewPager2) { tab, position ->
tab.text = names[position]
}.attach()
}
}
ExampleStateAdapter:
class ExampleStateAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int {
return 14
}
override fun createFragment(position: Int): Fragment {
Log.d("frg", "createFragment: $position")
return when (position) {
0 -> SomeFragment.newInstance("A")
1 -> SomeFragment.newInstance("B")
2 -> SomeFragment.newInstance("C")
3 -> SomeFragment.newInstance("D")
4 -> SomeFragment.newInstance("E")
5 -> SomeFragment.newInstance("F")
6 -> SomeFragment.newInstance("G")
7 -> SomeFragment.newInstance("H")
8 -> SomeFragment.newInstance("I")
9 -> SomeFragment.newInstance("J")
10 -> SomeFragment.newInstance("K")
11 -> SomeFragment.newInstance("L")
12 -> SomeFragment.newInstance("M")
13 -> SomeFragment.newInstance("N")
else -> SomeFragment.newInstance("0")
}
}
}
SomeFragment:
class SomeFragment : Fragment() {
var TAG = "frg 0"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
TAG = "frg ${arguments?.getString(CATEGORY)}"
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_some, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<TextView>(R.id.textView).apply {
text = arguments?.let {
it.getString(CATEGORY, "")
} ?: "not found"
}
}
override fun onStart() {
Log.d(TAG, "onStart()")
super.onStart()
}
override fun onResume() {
Log.d(TAG, "onResume()")
super.onResume()
}
override fun onPause() {
Log.d(TAG, "onPause()")
super.onPause()
}
override fun onStop() {
Log.d(TAG, "onStop()")
super.onStop()
}
companion object {
fun newInstance(
categoryName: String
) = SomeFragment().apply {
arguments = bundleOf(
CATEGORY to categoryName
)
}
}
}
fragment_some.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CDDC39"
tools:context=".fragments.SomeFragment">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fragment"
android:textSize="30sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tablayout" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn"
app:tabMode="scrollable" />
</androidx.constraintlayout.widget.ConstraintLayout>
dependencies:
// ViewPager2
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
// CardView
implementation 'androidx.cardview:cardview:1.0.0'
// Tablayout + other stuff...
implementation 'com.google.android.material:material:1.2.1'
I tried to put some logs and see what's happening. I have 14 Fragments in the list and I select tab on position 9.
Normal scenario (not stopping debugger in createFragment()
method):
2020-09-22 15:10:33.515 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 5
2020-09-22 15:10:33.534 26627-26627/com.example.viewpager2_example_2 D/frg F: onStart()
2020-09-22 15:10:33.552 26627-26627/com.example.viewpager2_example_2 D/frg D: onStop()
2020-09-22 15:10:33.573 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 6
2020-09-22 15:10:33.587 26627-26627/com.example.viewpager2_example_2 D/frg G: onStart()
2020-09-22 15:10:33.597 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 7
2020-09-22 15:10:33.609 26627-26627/com.example.viewpager2_example_2 D/frg H: onStart()
2020-09-22 15:10:33.633 26627-26627/com.example.viewpager2_example_2 D/frg C: onStop()
2020-09-22 15:10:33.636 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 8
2020-09-22 15:10:33.647 26627-26627/com.example.viewpager2_example_2 D/frg I: onStart()
2020-09-22 15:10:33.752 26627-26627/com.example.viewpager2_example_2 D/frg B: onStop()
2020-09-22 15:10:33.967 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 9
2020-09-22 15:10:33.972 26627-26627/com.example.viewpager2_example_2 D/frg A: onPause()
2020-09-22 15:10:33.973 26627-26627/com.example.viewpager2_example_2 D/frg A: onStop()
2020-09-22 15:10:34.018 26627-26627/com.example.viewpager2_example_2 D/frg F: onStop()
2020-09-22 15:10:34.023 26627-26627/com.example.viewpager2_example_2 D/frg I: onResume()
Problematic scenario (stopping debugger in createFragment()
method for 1-2 seconds each time):
2020-09-22 15:14:26.419 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 5
2020-09-22 15:14:28.551 26627-26627/com.example.viewpager2_example_2 D/frg F: onStart()
2020-09-22 15:14:28.577 26627-26627/com.example.viewpager2_example_2 D/frg D: onStop()
2020-09-22 15:14:28.602 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 6
2020-09-22 15:14:29.591 26627-26627/com.example.viewpager2_example_2 D/frg G: onStart()
2020-09-22 15:14:29.615 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 7
2020-09-22 15:14:30.962 26627-26627/com.example.viewpager2_example_2 D/frg H: onStart()
2020-09-22 15:14:30.999 26627-26627/com.example.viewpager2_example_2 D/frg C: onStop()
2020-09-22 15:14:31.003 26627-26627/com.example.viewpager2_example_2 D/frg B: onStop()
2020-09-22 15:14:31.006 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 8
2020-09-22 15:14:31.922 26627-26627/com.example.viewpager2_example_2 D/frg I: onStart()
2020-09-22 15:14:31.956 26627-26627/com.example.viewpager2_example_2 D/frg A: onPause()
2020-09-22 15:14:31.957 26627-26627/com.example.viewpager2_example_2 D/frg A: onStop()
2020-09-22 15:14:31.965 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 9
2020-09-22 15:14:33.210 26627-26627/com.example.viewpager2_example_2 D/frg J: onStart()
2020-09-22 15:14:33.240 26627-26627/com.example.viewpager2_example_2 D/frg G: onStop()
2020-09-22 15:14:33.248 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 10
2020-09-22 15:14:34.247 26627-26627/com.example.viewpager2_example_2 D/frg K: onStart()
2020-09-22 15:14:34.276 26627-26627/com.example.viewpager2_example_2 D/frg F: onStop()
2020-09-22 15:14:34.280 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 11
2020-09-22 15:14:35.286 26627-26627/com.example.viewpager2_example_2 D/frg L: onStart()
2020-09-22 15:14:35.316 26627-26627/com.example.viewpager2_example_2 D/frg H: onStop()
2020-09-22 15:14:35.320 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 12
2020-09-22 15:14:36.407 26627-26627/com.example.viewpager2_example_2 D/frg M: onStart()
2020-09-22 15:14:36.435 26627-26627/com.example.viewpager2_example_2 D/frg I: onStop()
2020-09-22 15:14:36.439 26627-26627/com.example.viewpager2_example_2 D/frg: createFragment: 13
2020-09-22 15:14:37.632 26627-26627/com.example.viewpager2_example_2 D/frg N: onStart()
2020-09-22 15:14:37.656 26627-26627/com.example.viewpager2_example_2 D/frg J: onStop()
Anyone can explain what sort of sorcery is happening here inside ViewPager2
?