How to use TabLayout with ViewPager2 in Android

JavaAndroidAndroidxAndroid TablayoutAndroid Viewpager2

Java Problem Overview


I want to use com.google.android.material.tabs.TabLayout component with Android's new ViewPager implementation androidx.viewpager2.widget.ViewPager2. However, the setupWithViewPager(..) method provided by TabLayout supports only the old ViewPager implementation. Is there a way to bind a TabLayout to a ViewPager2 component easily?

Java Solutions


Solution 1 - Java

You have to use this TabLayoutMediator that mimics tabLayout.setupWithViewPager() and sets up the ViewPager2 with Tablayout. Otherwise, you will have to write your own adapter that will combine both parties.

Its code will look like this in Kotlin

TabLayoutMediator(tabLayout, viewPager) { tab, position ->
  tab.text = tabTitles[position]
}.attach()

Solution 2 - Java

No hacks, no extensions, no TabLayoutMediator

I am on implementation 'com.google.android.material:material:1.2.0-alpha02' and do the following without needing the TabLayoutMediator. Instead, I link the TabLayout with the ViewPager2 using the method described here. I've also added a working example to github here. I think I've minimized the solution to a minimal working example. I'll explain the important bits.

Adding the elements to the template

First we'll need to add the TabLayout and ViewPager2 to the layout. I've placed them inside a LinearLayout and CoordinatorLayout here, but you can do whatever you like of course.

<!-- 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.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <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/pager"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"/>

        </LinearLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Connecting an adapter to the viewpager

So the adapter is in charge of supplying the correct fragments to the activity. You'll have to extend FragmentStateAdapter which I've done very simply as below (it's a private class because it's declared within my MainActivity.java here):

    private class ViewStateAdapter extends FragmentStateAdapter {

        public ViewStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle) {
            super(fragmentManager, lifecycle);
        }

        @NonNull
        @Override
        public Fragment createFragment(int position) {
            // Hardcoded in this order, you'll want to use lists and make sure the titles match
            if (position == 0) {
                return new BarFragment();
            }
            return new FooFragment();
        }

        @Override
        public int getItemCount() {
            // Hardcoded, use lists
            return 2;
        }
    }

I can then connect my own Adapter to the ViewPager as below:

FragmentManager fm = getSupportFragmentManager();
ViewStateAdapter sa = new ViewStateAdapter(fm, getLifecycle());
final ViewPager2 pa = findViewById(R.id.pager);
pa.setAdapter(sa);

I've added the fragments to my viewpager. (Because I hardcoded the Fragments in my adapter, you should use a list and something like an 'addFragment' method or something)

The TabLayout

Then with

TabLayout tabLayout = findViewById(R.id.tabLayout);
tabLayout.addTab(tabLayout.newTab().setText("Bar"));
tabLayout.addTab(tabLayout.newTab().setText("Foo"));

I add two tabs to my TabLayout, showing the titles but not letting me switch to the fragments yet.

Connecting TabLayout to Adapter
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                pa.setCurrentItem(tab.getPosition());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });

This should be pretty straightforward. User clicks on a tab, I get the position in my callback and I simply set the adapter's current item to that position.

Change Tab when swiping

Finally we couple back when the user swipes the fragment to set the correct tab item as selected

pa.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
    @Override
    public void onPageSelected(int position) {
        tabLayout.selectTab(tabLayout.getTabAt(position));
    }
});

Solution 3 - Java

UPDATE

># check this Create swipe views with tabs using ViewPager2

Here is the Updated answer How to use TabLayout with ViewPager2 in Android

Now we no need to create a class from TabLayoutMediator

Use below dependencies

implementation 'com.google.android.material:material:1.1.0-alpha08'
implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'

SAMPLE CODE

>XMl layout

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
        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">

    <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:layout_scrollFlags="scroll|enterAlways"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

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

    <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager"
            app:layout_anchor="@id/tabs"
            app:layout_anchorGravity="bottom"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
    />


</androidx.coordinatorlayout.widget.CoordinatorLayout>

>Activity

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
import com.google.android.material.tabs.TabLayoutMediator

import com.google.android.material.tabs.TabLayout


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

//        setSupportActionBar(toolbar)
        viewpager.adapter = AppViewPagerAdapter(supportFragmentManager, lifecycle)

        TabLayoutMediator(tabs, viewpager, object : TabLayoutMediator.OnConfigureTabCallback {
            override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
                // Styling each tab here
                tab.text = "Tab $position"
            }
        }).attach()


    }
}

UPDATE

If your using implementation 'com.google.android.material:material:1.1.0-alpha10' then use below code

        TabLayoutMediator(tabs, viewpage,
        TabLayoutMediator.TabConfigurationStrategy { tab, position ->
            when (position) {
                0 -> { tab.text = "TAB ONE"}
                1 -> { tab.text = "TAB TWO"}
            }
        }).attach()

> OUTPUT

enter image description here

Solution 4 - Java

Initialize the TabLayoutMediator object with an object of TabLayout, ViewPager2 , autoRefresh -- boolean type, and an object of OnConfigurationChangeCallback.

TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2, true, new TabLayoutMediator.OnConfigureTabCallback() {
  @Override
  public void onConfigureTab(TabLayout.Tab tab, int position) {
    // position of the current tab and that tab  
  }
});

Finally just call attach() to the TabLayoutMediator object to wire up the tablayout to the viewpager :-

 tabLayoutMediator.attach();

autoRefresh - key if set to true -- ( By default its set to true )

>RECREATES all the tabs of the tabLayout if notifyDataSetChanged is called to the viewpager adapter.

Use the contents of TabLayoutMediator.java

Solution 5 - Java

You can use Kotlin extension function:

fun TabLayout.setupWithViewPager(viewPager: ViewPager2, labels: List<String>) {

    if (labels.size != viewPager.adapter?.itemCount)
        throw Exception("The size of list and the tab count should be equal!")

    TabLayoutMediator(this, viewPager,
        TabLayoutMediator.TabConfigurationStrategy { tab, position ->
            tab.text = labels[position]
        }).attach()
}


  

And call it:

 tabLayout.setupWithViewPager(viewPager, listOf("Tab A", "Tab B"))

Solution 6 - Java

If you're coding in JAVA. You can use the following adapter for the viewPager2:

public class ViewPagerAdapter extends FragmentStateAdapter {
  private static final int CARD_ITEM_SIZE = 10;
  public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
    super(fragmentActivity);
  }
  @NonNull @Override public Fragment createFragment(int position) {
    return CardFragment.newInstance(position);
  }
  @Override public int getItemCount() {
    return CARD_ITEM_SIZE;
  }
}

And on the mainActivity you need to have something inline with the following:

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    viewPager = findViewById(R.id.view_pager);
    tabLayout = findViewById(R.id.tabs);
    viewPager.setAdapter(new ViewPagerAdapter(this));
    new TabLayoutMediator(tabLayout, viewPager,
        new TabLayoutMediator.TabConfigurationStrategy() {
          @Override public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
            tab.setText("Tab " + (position + 1));
          }
        }).attach();
  }
}

Solution 7 - Java

it is very simple:

java code:

tabLayout=findViewById(R.id.tabLayout);
    viewPager=findViewById(R.id.viewPager);
    viewPager.setAdapter(new ViewPagerAdapter(this));

    new TabLayoutMediator(tabLayout, viewPager, new TabLayoutMediator.TabConfigurationStrategy() {
        @Override
        public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
            if (position==0){
                tab.setText(R.string.photos);
                tab.view.setBackground(getResources().getDrawable(R.drawable.ic_rectangle_1345));
            }
            else {
                tab.setText(R.string.videos);
            }
        }
    }).attach();

    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            tab.view.setBackground(getResources().getDrawable(R.drawable.ic_rectangle_1345));
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
            tab.view.setBackgroundColor(Color.TRANSPARENT);
        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

adapter:

public class ViewPagerAdapter extends FragmentStateAdapter {
public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
    super(fragmentActivity);
}

@NonNull
@Override
public Fragment createFragment(int position) {
    if (position==0) {
        return new PhotosFragment();
    }
    else {
        return new VideosFragment();
    }
}

@Override
public int getItemCount() {
    return 2;
}}

XML:

<com.google.android.material.tabs.TabLayout
    android:id="@+id/tabLayout"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:background="@color/tool_bar_bg"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/collection_bar" />

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    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" />

Solution 8 - Java

If your tab title comes from string.xml.
You can put the fragment with title together in a List then set title by TabLayoutMediator. It helps you easily to reorder, delete or add new fragment

class MyFragment : Fragment() {

    override fun onCreateView(...): View? {
        ...

        TabLayoutMediator(tabLayout, viewPager) { tab, position ->
            tab.text = pagerAdapter.getTabTitle(position)
        }.attach()
    }

    private inner class PagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
        val pages = listOf(
            Pair(HomeFragment.newInstance(), R.string.home),
            Pair(GraphFragment.newInstance(), R.string.graph),
            Pair(SettingFragment.newInstance(), R.string.setting),
        )

        override fun createFragment(position: Int): Fragment {
            return pages[position].first
        }

        override fun getItemCount(): Int {
            return pages.count()
        }

        fun getTabTitle(position: Int): String {
            return getString(pages[position].second)
        }
    }
}

Solution 9 - Java

Im running mine on the implementation 'com.google.android.material:material:1.2.1' and didn't use the tabLayoutMediator either. For starters the https://developer.android.com/jetpack/androidx/migrate/class-mappings have changed for the TabLayout in androidX so be sure to be using com.google.android.material.tabs.TabLayout in your import statement. Heres the rest of my implementation of the solution: The Xml layout declaration of the viewPager and the TabLayout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/tan_background"
    android:orientation="vertical">

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

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager"
        app:layout_anchor="@id/tabs"
        app:layout_anchorGravity="bottom"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

and the activity file

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Set the content of the activity to use the activity_main.xml layout file
        setContentView(layout.activity_main);

        // Find the view pager that will allow the user to swipe between fragments
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

        // Create an adapter that knows which fragment should be shown on each page
        SimpleFragmentPagerAdapter adapter = new SimpleFragmentPagerAdapter(this,getSupportFragmentManager());

        // Set the adapter onto the view pager
        viewPager.setAdapter(adapter);

        // Find the tab layout that shows the tabs
        TabLayout tabLayout = findViewById(R.id.tabs);

        // Connect the tab layout with the view pager. This will
        //   1. Update the tab layout when the view pager is swiped
        //   2. Update the view pager when a tab is selected
        //   3. Set the tab layout's tab names with the view pager's adapter's titles
        //      by calling onPageTitle()
        tabLayout.setupWithViewPager(viewPager);
    }
}

I used a fragment adapter setup in a different class as well where I passed the context to the constructor for the different Tabs

Solution 10 - Java

Make sure you use TabLayoutMediator after your set adapter to view_pager2

TabLayoutMediator(tab_layout, view_pager2) { tab, position ->
                tab.text = fragmentList[position].second  // here u can modify you text..
            }.attach()

Solution 11 - Java

You layouts are ready, fragments are ready and Adapters are ready. All you need now is to setup an event listener to

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){

        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            viewPager.setCurrentItem(tab.getPosition());
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

This should bind the Tab layout to your ViewPager2.

Solution 12 - Java

You can take inspiration from my sample project to create UIs with Viewpager2. I use TabLayoutMediator on all examples, Simple and very useful.  https://github.com/gabriel-TheCode/OnboardingViewPagerExamples.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionUgurcan YildirimView Question on Stackoverflow
Solution 1 - JavaNikola DespotoskiView Answer on Stackoverflow
Solution 2 - JavaPotential GuacamoleView Answer on Stackoverflow
Solution 3 - JavaAskNileshView Answer on Stackoverflow
Solution 4 - JavaSantanu SurView Answer on Stackoverflow
Solution 5 - Javaseyfullah.bilginView Answer on Stackoverflow
Solution 6 - JavaMiguel TomásView Answer on Stackoverflow
Solution 7 - JavaAli AslamView Answer on Stackoverflow
Solution 8 - JavaLinhView Answer on Stackoverflow
Solution 9 - JavaKimView Answer on Stackoverflow
Solution 10 - JavaabhiView Answer on Stackoverflow
Solution 11 - JavaP S RaoView Answer on Stackoverflow
Solution 12 - JavaGabriel TheCodeView Answer on Stackoverflow