Migrating from ViewPager to ViewPager2 with FragmentStateAdapter causes visible screen yank and StrictMode to be triggered

I've followed the Android documentation and this tutorial to migrate to ViewPager2. When swiping the 4 screens from left to right, there is a visible screen yank which is not a great user experience. I've used strictmode to measure the performance.

The documentation explicitly mentions that createFragment must provide a new Fragment each time so the implementation seems to be implemented correct.

MainActivity:

private fun initializeViewPager(items: List<Item>) {
    val tabAdapter = TabAdapter(this, items)
    viewPager2.adapter = tabAdapter

    TabLayoutMediator(tabLayout, viewPager2) { tab, position ->
        tab.text = items[position].abbreviation
    }.attach()


class TabAdapter(activity: AppCompatActivity, private val items: List<Item>) :
    FragmentStateAdapter(activity) {

    override fun getItemCount(): Int {
        return items.size
    }

    override fun getItemId(position: Int): Long {
        return items[position].getId()
    }

    override fun createFragment(position: Int): Fragment {
        return VersesFragment.getInstance(items[position])
    }
}

SomeFragment:

class SomeFragment : DaggerFragment()  {

    var adapter: ItemAdapter? = null

    private lateinit var binding: FragmentSomeBinding

    private lateinit var item: Item

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        handleArguments()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        binding = FragmentSomeBinding.inflate(inflater, container, false).apply {
            lifecycleOwner = viewLifecycleOwner
        }

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // 
        .. Setting of view elements using the serialized item
        //
    }

    private fun handleArguments() {
        val args = arguments
            ?: throw NullPointerException("getArguments() seems to be null. ARG_BIBLETRANSLATION needed!")

        if (!args.containsKey(ARG_BIBLETRANSLATION)) throw NullPointerException("getArguments() does not contain ARG_BIBLETRANSLATION!")

        item = args[ARG_BIBLETRANSLATION] as Item
    }

    companion object {

        const val ARG_MY_ITEM = "arg_my_item"

        fun getInstance(item: Item): Fragment {

            val fragment = VersesFragment()

            fragment.arguments = Bundle(2).apply {
                putSerializable(ARG_MY_ITEM, item)
            }

            return fragment
        }
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>
</layout>

Just for completeness, here is the original code that does perform fine:

MainActivity:

val adapter = SomePagerAdapter(items as ArrayList<Item>)
viewPager.adapter = sdapter

SomePagerAdapter:

class SomePagerAdapter(private val items: ArrayList<Item>) : PagerAdapter() {
    
    override fun instantiateItem(container: ViewGroup, position: Int): Any { 
        // Same code as in SomeFragment
    }
}

The tutorial from raywenderlich is super simple, but when enabling strictmode, it does get triggered, while strictmode does not get triggered with the old viewpager and the same code:

  private fun enableStrictMode() {
    StrictMode.setThreadPolicy(
        StrictMode.ThreadPolicy.Builder()
            .detectDiskReads()
            .detectDiskWrites()
            .detectNetwork()
            .penaltyLog()
            .build()
    )

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
      StrictMode.VmPolicy.Builder()
          .detectNonSdkApiUsage()
          .detectAll()
    }
  }


Read more here: https://stackoverflow.com/questions/64892174/migrating-from-viewpager-to-viewpager2-with-fragmentstateadapter-causes-visible

Content Attribution

This content was originally published by Jim Clermonts at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: