TabLayout
Tab Selection
Android Development
User Interface
Android UI Components

TabLayout tab selection

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

TabLayout from the Material Components library lets you create horizontal tabs in Android. To programmatically select a tab, call tabLayout.getTabAt(index).select(). To respond to selection changes, add an OnTabSelectedListener. TabLayout is typically paired with ViewPager2 to synchronize tab selection with page swiping using TabLayoutMediator. The key distinction is between user-driven selection (taps and swipes) and programmatic selection (your code calling select()).

Basic TabLayout Setup

xml
1<!-- activity_main.xml -->
2<com.google.android.material.tabs.TabLayout
3    android:id="@+id/tabLayout"
4    android:layout_width="match_parent"
5    android:layout_height="wrap_content"
6    app:tabMode="fixed"
7    app:tabGravity="fill">
8
9    <com.google.android.material.tabs.TabItem
10        android:layout_width="wrap_content"
11        android:layout_height="wrap_content"
12        android:text="Home" />
13
14    <com.google.android.material.tabs.TabItem
15        android:layout_width="wrap_content"
16        android:layout_height="wrap_content"
17        android:text="Profile" />
18
19    <com.google.android.material.tabs.TabItem
20        android:layout_width="wrap_content"
21        android:layout_height="wrap_content"
22        android:text="Settings" />
23</com.google.android.material.tabs.TabLayout>
kotlin
// Programmatically select a tab
val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
tabLayout.getTabAt(1)?.select()  // Select "Profile" tab

Tab Selection Listener

kotlin
1tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
2    override fun onTabSelected(tab: TabLayout.Tab) {
3        // Called when a tab is selected (by user tap or programmatically)
4        when (tab.position) {
5            0 -> showFragment(HomeFragment())
6            1 -> showFragment(ProfileFragment())
7            2 -> showFragment(SettingsFragment())
8        }
9    }
10
11    override fun onTabUnselected(tab: TabLayout.Tab) {
12        // Called when a previously selected tab is unselected
13    }
14
15    override fun onTabReselected(tab: TabLayout.Tab) {
16        // Called when an already-selected tab is tapped again
17        // Common use: scroll to top or refresh content
18    }
19})
20
21private fun showFragment(fragment: Fragment) {
22    supportFragmentManager.beginTransaction()
23        .replace(R.id.fragmentContainer, fragment)
24        .commit()
25}

TabLayout with ViewPager2

xml
1<com.google.android.material.tabs.TabLayout
2    android:id="@+id/tabLayout"
3    android:layout_width="match_parent"
4    android:layout_height="wrap_content" />
5
6<androidx.viewpager2.widget.ViewPager2
7    android:id="@+id/viewPager"
8    android:layout_width="match_parent"
9    android:layout_height="0dp"
10    app:layout_constraintTop_toBottomOf="@id/tabLayout"
11    app:layout_constraintBottom_toBottomOf="parent" />
kotlin
1val viewPager = findViewById<ViewPager2>(R.id.viewPager)
2val tabLayout = findViewById<TabLayout>(R.id.tabLayout)
3
4viewPager.adapter = object : FragmentStateAdapter(this) {
5    override fun getItemCount() = 3
6    override fun createFragment(position: Int) = when (position) {
7        0 -> HomeFragment()
8        1 -> ProfileFragment()
9        else -> SettingsFragment()
10    }
11}
12
13// Sync TabLayout with ViewPager2
14TabLayoutMediator(tabLayout, viewPager) { tab, position ->
15    tab.text = when (position) {
16        0 -> "Home"
17        1 -> "Profile"
18        else -> "Settings"
19    }
20}.attach()

TabLayoutMediator handles bidirectional sync — tapping a tab scrolls the pager, and swiping the pager updates the selected tab.

Custom Tab Views

kotlin
1// Add tabs with icons and custom views
2val tab = tabLayout.newTab()
3tab.setIcon(R.drawable.ic_home)
4tab.text = "Home"
5tabLayout.addTab(tab)
6
7// Fully custom tab view
8val customTab = tabLayout.newTab()
9val customView = LayoutInflater.from(this)
10    .inflate(R.layout.custom_tab, tabLayout, false)
11customView.findViewById<TextView>(R.id.tabText).text = "Messages"
12customView.findViewById<TextView>(R.id.tabBadge).text = "3"
13customTab.customView = customView
14tabLayout.addTab(customTab)
15
16// Update badge count on a custom tab
17fun updateBadge(tabIndex: Int, count: Int) {
18    val tab = tabLayout.getTabAt(tabIndex)
19    val badge = tab?.customView?.findViewById<TextView>(R.id.tabBadge)
20    badge?.text = if (count > 0) count.toString() else ""
21    badge?.visibility = if (count > 0) View.VISIBLE else View.GONE
22}

Programmatic Tab Selection

kotlin
1// Select by index
2tabLayout.getTabAt(2)?.select()
3
4// Select after a delay (e.g., after data loads)
5tabLayout.post {
6    tabLayout.getTabAt(1)?.select()
7}
8
9// Select without triggering the listener
10// (No built-in way — use a flag)
11var isUserAction = true
12
13tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
14    override fun onTabSelected(tab: TabLayout.Tab) {
15        if (isUserAction) {
16            handleTabChange(tab.position)
17        }
18    }
19    override fun onTabUnselected(tab: TabLayout.Tab) {}
20    override fun onTabReselected(tab: TabLayout.Tab) {}
21})
22
23// Programmatic selection without triggering handler
24isUserAction = false
25tabLayout.getTabAt(0)?.select()
26isUserAction = true

Styling Tabs

xml
1<com.google.android.material.tabs.TabLayout
2    android:id="@+id/tabLayout"
3    android:layout_width="match_parent"
4    android:layout_height="wrap_content"
5    app:tabMode="scrollable"
6    app:tabGravity="start"
7    app:tabIndicatorColor="@color/primary"
8    app:tabIndicatorHeight="3dp"
9    app:tabSelectedTextColor="@color/primary"
10    app:tabTextColor="@color/gray"
11    app:tabRippleColor="@color/ripple"
12    app:tabIndicatorFullWidth="false"
13    app:tabIndicatorAnimationMode="elastic" />

Common Pitfalls

  • Calling getTabAt(index).select() before tabs are added: If you select a tab in onCreate before the ViewPager adapter is set and TabLayoutMediator.attach() is called, the selection is overridden. Call select() after attach() or use tabLayout.post { ... } to defer the selection.
  • Listener firing during programmatic selection: onTabSelected fires both for user taps and programmatic select() calls. If your listener triggers network requests or navigation, it runs unexpectedly during setup. Use a boolean flag or remove/re-add the listener around programmatic selection.
  • Using ViewPager (1) instead of ViewPager2: The original ViewPager uses TabLayout.setupWithViewPager(), while ViewPager2 uses TabLayoutMediator. Mixing them up (e.g., calling setupWithViewPager with a ViewPager2) causes compilation errors or runtime crashes. Use the API that matches your pager.
  • Tab indicator not showing on scrollable TabLayout: When tabMode="scrollable" is set and there are few tabs, the indicator may appear misaligned or missing. Ensure tabGravity is set to "start" (not "fill") with scrollable mode, and verify that tab text is not empty — empty tabs have zero width.
  • Memory leaks from OnTabSelectedListener in Fragments: Adding a listener in onViewCreated without removing it in onDestroyView causes the fragment to leak. Store the listener in a variable and call tabLayout.removeOnTabSelectedListener(listener) in onDestroyView.

Summary

  • Use tabLayout.getTabAt(index)?.select() to programmatically select a tab
  • Add OnTabSelectedListener to respond to tab changes from both taps and programmatic selection
  • Pair TabLayout with ViewPager2 using TabLayoutMediator for swipe-to-tab synchronization
  • Use tabLayout.post { } to defer programmatic selection until layout is complete
  • Remove tab listeners in onDestroyView to prevent memory leaks in fragments

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.