Skip to content
intermediate Phase 3 · UI with XML & Views

ViewPager2

Implement swipeable screens with ViewPager2 and TabLayout integration.

40m
2 problems
Topic Progress 0%

ViewPager2 Fundamentals

Why ViewPager2 Replaced ViewPager

The original ViewPager had two architectural problems: it only supported horizontal scrolling, and its adapter management was error-prone. ViewPager2 fixes both by:

  • Supporting both horizontal and vertical scrolling
  • Using RecyclerView's adapter architecture (ListAdapter + DiffUtil)
  • Supporting RTL (right-to-left) layouts natively
  • Providing better Fragment lifecycle management

Adding ViewPager2

Add the dependency:

dependencies {
    implementation("androidx.viewpager2:viewpager2:1.1.0")
}

Basic XML Setup

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent" />

Fragment-Based Pages

ViewPager2 works with Fragments via FragmentStateAdapter. Each page is a Fragment, and the adapter manages their lifecycle:

class OnboardingAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    override fun getItemCount() = 3

    override fun createFragment(position: Int): Fragment {
        return when (position) {
            0 -> WelcomeFragment()
            1 -> FeaturesFragment()
            2 -> GetStartedFragment()
            else -> throw IllegalArgumentException("Invalid position: $position")
        }
    }
}

Set it up in your Fragment:

class OnboardingFragment : Fragment() {
    private var _binding: FragmentOnboardingBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
    ): View {
        _binding = FragmentOnboardingBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.viewPager.adapter = OnboardingAdapter(this)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}

RecyclerView-Based Pages

ViewPager2 also supports non-Fragment pages using RecyclerView adapters. Use this when your pages are simple views, not Fragments:

class ImagePagerAdapter(
    private val images: List<Int>
) : RecyclerView.Adapter<ImagePagerAdapter.ImageViewHolder>() {

    inner class ImageViewHolder(private val imageView: ImageView) :
        RecyclerView.ViewHolder(imageView) {
        fun bind(resId: Int) {
            imageView.setImageResource(resId)
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageViewHolder {
        val imageView = ImageView(parent.context).apply {
            layoutParams = ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
            scaleType = ImageView.ScaleType.CENTER_CROP
        }
        return ImageViewHolder(imageView)
    }

    override fun onBindViewHolder(holder: ImageViewHolder, position: Int) {
        holder.bind(images[position])
    }

    override fun getItemCount() = images.size
}

Listening to Page Changes

binding.viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        // Update UI when page changes (e.g., highlight tab)
    }

    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        // Animate elements based on scroll position
    }

    override fun onPageScrollStateChanged(state: Int) {
        // SCROLL_STATE_IDLE, SCROLL_STATE_DRAGGING, SCROLL_STATE_SETTLING
    }
})

TabLayout Integration and Page Transformations

TabLayout + ViewPager2

TabLayout provides tab indicators above or below the ViewPager2. Connect them with TabLayoutMediator:

<com.google.android.material.tabs.TabLayout
    android:id="@+id/tabLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintTop_toBottomOf="@id/tabLayout"
    app:layout_constraintBottom_toBottomOf="parent" />
TabLayoutMediator(binding.tabLayout, binding.viewPager) { tab, position ->
    tab.text = when (position) {
        0 -> "Home"
        1 -> "Search"
        2 -> "Profile"
        else -> ""
    }
    // Optional: set tab icon instead of text
    // tab.setIcon(R.drawable.ic_home)
}.attach()

Tab Modes

Mode Behavior
TabLayout.MODE_FIXED All tabs same width, evenly distributed (2-5 tabs)
TabLayout.MODE_SCROLLABLE Tabs scroll horizontally (5+ tabs)

Set in XML:

<com.google.android.material.tabs.TabLayout
    app:tabMode="scrollable"
    app:tabGravity="fill" />

Page Transformations

Animate page transitions with setPageTransformer:

binding.viewPager.setPageTransformer { page, position ->
    // Simple depth effect
    page.alpha = when {
        position < -1 -> 0f
        position <= 0 -> 1f
        position <= 1 -> 1f - position
        else -> 0f
    }

    // Parallax effect
    page.translationX = position * -50

    // Scale effect
    val scale = 1f - abs(position) * 0.2f
    page.scaleX = scale
    page.scaleY = scale
}

The position parameter represents the page's relative position: 0 means fully visible, -1 means off-screen left, 1 means off-screen right. Intermediate values represent partially visible pages.

ViewPager2 vs ViewPager vs HorizontalScrollView

Feature ViewPager2 ViewPager HorizontalScrollView
Vertical scrolling Yes No No
RTL support Yes No No
Fragment lifecycle Proper Fragile N/A
Adapter RecyclerView-based Custom N/A
DiffUtil Built-in No N/A
Tab integration TabLayoutMediator Manual No

Always prefer ViewPager2 for new projects. ViewPager is deprecated.

Quiz

1. Why was ViewPager2 created to replace ViewPager?

Question 1 options

2. What class connects ViewPager2 with TabLayout?

Question 2 options

3. In a PageTransformer, what does a position of 0 mean?

Question 3 options

4. What adapter class should you use for Fragment-based pages in ViewPager2?

Question 4 options

Flashcards

Question

What are the main improvements of ViewPager2 over ViewPager?

Answer

Vertical scrolling, RTL support, RecyclerView-based adapter (DiffUtil), proper Fragment lifecycle management, and TabLayoutMediator for tab integration.

Question

What does TabLayoutMediator do?

Answer

Connects ViewPager2 with TabLayout, synchronizing tab selection with page changes. It takes a lambda to set tab text or icons based on page position.

Question

What is the difference between TabLayout.MODE_FIXED and MODE_SCROLLABLE?

Answer

MODE_FIXED gives all tabs equal width (best for 2-5 tabs). MODE_SCROLLABLE lets tabs scroll horizontally (best for 5+ tabs).

Question

When should you use a RecyclerView adapter instead of FragmentStateAdapter?

Answer

When pages are simple views (images, text layouts) that don't need Fragment lifecycle management. FragmentStateAdapter is for pages that are Fragments.

Revision Notes

Key Takeaways

  • 1. ViewPager2 replaces ViewPager with vertical scrolling, RTL support, and RecyclerView architecture
  • 2. Use FragmentStateAdapter for Fragment-based pages and RecyclerView.Adapter for simple view pages
  • 3. TabLayoutMediator connects ViewPager2 with TabLayout for synchronized tab navigation
  • 4. PageTransformer position 0 means fully visible — use it to animate page transitions
  • 5. Always prefer ViewPager2 over the deprecated ViewPager for new projects

Interview Tips

  • Explain why ViewPager2 was created — mention vertical scrolling and RecyclerView architecture
  • Be ready to describe the TabLayoutMediator pattern for tab integration
  • Know the PageTransformer position values: 0 is visible, -1 left, +1 right
  • Discuss when to use FragmentStateAdapter vs RecyclerView.Adapter in ViewPager2

Cheat Sheet

ViewPager2 Cheat Sheet

Setup:

viewPager.adapter = MyFragmentAdapter(this)
TabLayoutMediator(tabLayout, viewPager) { tab, pos ->
    tab.text = titles[pos]
}.attach()

FragmentStateAdapter:

  • For Fragment-based pages
  • Override createFragment() and getItemCount()
  • Pass the host Fragment to the constructor

TabLayout modes:

  • MODE_FIXED: equal width tabs (2-5)
  • MODE_SCROLLABLE: scrollable tabs (5+)

PageTransformer:

  • position 0 = fully visible
  • position -1 = off-screen left
  • position 1 = off-screen right
  • Use alpha, translationX, scaleX/Y for effects

Key differences from ViewPager:

  • Vertical scrolling
  • RTL support
  • RecyclerView adapter architecture
  • DiffUtil built-in
  • ViewPager is deprecated