Creating a Tabbed Interface using the TabLayout Component in Java

The previous chapter outlined the concept of material design in Android. It introduced two of the components provided by the design support library in the form of the floating action button and the Snackbar. This chapter will demonstrate how to use another of the design library components, the TabLayout, which can be combined with the ViewPager class to create a tab-based interface within an Android activity.

An Introduction to the ViewPager2

Although not part of the design support library, ViewPager2 is a useful companion class when used with the TabLayout component to implement a tabbed user interface. The primary role of ViewPager2 is to allow the user to flip through different pages of information where a layout fragment most typically represents each page. The fragments associated with ViewPager2 are managed by an instance of the FragmentStateAdapter class.

At a minimum, the pager adapter assigned to ViewPager2 must implement two methods. The first, named getItemCount(), must return the total number of page fragments to be displayed to the user. The second method, createFragment(), is passed a page number and must return the corresponding fragment object ready to be presented to the user.

An Overview of the TabLayout Component

As previously discussed, TabLayout is one of the components introduced in material design and is included in the design support library. The purpose of the TabLayout is to present the user with a row of tabs that can be selected to display different pages to the user. The tabs can be fixed or scrollable, whereby the user can swipe left or right to view more tabs than will currently fit on the display. The information displayed on a tab can be text-based, an image, or a combination of text and images. Figure 46-1, for example, shows the tab bar for an app consisting of four tabs displaying images:

Figure 46-1

Figure 46-2, on the other hand, shows a TabLayout configuration consisting of four tabs displaying text in a scrollable configuration:

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Figure 46-2

The remainder of this chapter will work through creating an example project that demonstrates the TabLayout component together with a ViewPager2 and four fragments.

Creating the TabLayoutDemo Project

Select the New Project option from the welcome screen and, within the resulting new project dialog, choose the Basic Views Activity template before clicking on the Next button.

Enter TabLayoutDemo into the Name field and specify com.ebookfrenzy.tablayoutdemo as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo) and the Language menu to Java.

Once the project has been created, load the content_main.xml file into the Layout Editor tool, select the NavHostFragment object, and then delete it. Since we will not be using the navigation features of the Basic Views Activity template, edit the MainActivity.java file and modify the onCreate() method to remove the navigation code:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    binding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(binding.getRoot());
 
    setSupportActionBar(binding.toolbar);
 
    // NavController navController = 
    //    Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
    // appBarConfiguration = 
    //    new AppBarConfiguration.Builder(navController.getGraph()).build();
    // NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
 
    binding.fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                    .setAction("Action", null).show();
        }
    });
}Code language: Java (java)

Finally, delete the onSupportNavigateUp() method.

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Creating the First Fragment

Each tab on the TabLayout will display a different fragment when selected. Create the first of these fragments by right-clicking on the app -> java -> com.ebookfrenzy.tablayoutdemo entry in the Project tool window and selecting the New -> Fragment -> Fragment (Blank) option. In the resulting dialog, enter Tab1Fragment into the Fragment Name: field and fragment_tab1 into the Fragment Layout Name: field. Click on the Finish button to create the new fragment:

Figure 46-3

Edit the Tab1Fragment.java file, and if Android Studio has not added one automatically, add an OnFragmentInteractionListener interface declaration as follows:

.
.
import android.net.Uri;
.
.
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
.
.Code language: Java (java)

Load the newly created fragment_tab1.xml file (located under app -> res -> layout) into the Layout Editor tool, right-click on the FrameLayout entry in the Component Tree panel, and select the Convert FrameLayout to ConstraintLayout menu option. In the resulting dialog, verify that all conversion options are selected before clicking on OK. Change the ID of the layout to constraintLayout.

Once the layout has been converted to a ConstraintLayout, delete the TextView from the layout. From the Palette, locate the TextView widget and drag and drop it so it is positioned in the center of the layout. Edit the object’s text property to read “Tab 1 Fragment”, extract the string to a resource named tab_1_fragment, and click the Infer Constraints toolbar button. At this point, the layout should match that of Figure 46-4:

Figure 46-4

Duplicating the Fragments

So far, the project contains one of the four required fragments. It would be quicker to duplicate the first fragment instead of creating the remaining three fragments using the previous steps. Each fragment consists of a layout XML file and a Java class file, each needing to be duplicated.

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Right-click on the fragment_tab1.xml file in the Project tool window and select the Copy option from the resulting menu. Right-click on the layout entry, this time selecting the Paste option. Name the new layout file fragment_tab2.xml in the resulting dialog before clicking the OK button. Edit the new fragment_tab2.xml file and change the text on the Text View to “Tab 2 Fragment”, following the usual steps to extract the string to a resource named tab_2_fragment.

To duplicate the Tab1Fragment class file, right-click on the class listed under app -> java -> com.ebookfrenzy. tablayoutdemo and select Copy. Right-click on the com.ebookfrenzy.tablayoutdemo entry and select Paste. In the Copy Class dialog, enter Tab2Fragment into the New name: field and click OK.

Edit the new Tab2Fragment.java file and modify the onCreateView() method to inflate the fragment_tab2 layout file:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_tab2, container, false);
}Code language: Java (java)

Perform the above duplication steps twice to create the fragment layout and class files for the remaining two fragments. On completion of these steps, the project structure should match that of Figure 46-5:

Figure 46-5

Adding the TabLayout and ViewPager2

With the fragment creation process now complete, the next step is to add the TabLayout and ViewPager2 to the main activity layout file. Edit the activity_main.xml file and add these elements as outlined in the following XML listing. Note that the TabLayout component is embedded into the AppBarLayout element while the ViewPager2 is placed after the AppBarLayout:

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

<?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"
    tools:context=".MainActivity">
 
    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/Theme.TabLayoutDemo.AppBarOverlay">
 
        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/Theme.TabLayoutDemo.PopupOverlay" />
 
        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabGravity="fill"/>
        
    </com.google.android.material.appbar.AppBarLayout>
 
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
    
    <include layout="@layout/content_main" />
 
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_marginEnd="@dimen/fab_margin"
        android:layout_marginBottom="16dp"
        app:srcCompat="@android:drawable/ic_dialog_email" />
 
</androidx.coordinatorlayout.widget.CoordinatorLayout>Code language: HTML, XML (xml)

Creating the Pager Adapter

This example will use the ViewPager2 approach to handling the fragments assigned to the TabLayout tabs, with ViewPager2 added to the layout resource file, a new class which subclasses FragmentStateAdapter needs to be added to the project to manage the fragments that will be displayed when the user selects the tab items.

Add a new class to the project by right-clicking on the com.ebookfrenzy.tablayoutdemo entry in the Project tool window and selecting the New -> Java Class menu option. In the new class dialog, enter TabPagerAdapter into the Name: field, select the Class item in the list, and press the keyboard Return key. Edit the TabPagerAdapter.java file so that it reads as follows:

package com.ebookfrenzy.tablayoutdemo;
 
import androidx.annotation.NonNull;
import androidx.fragment.app.*;
import androidx.viewpager2.adapter.FragmentStateAdapter;
 
public class TabPagerAdapter extends FragmentStateAdapter {
 
    int tabCount;
 
    public TabPagerAdapter(@NonNull FragmentActivity fragmentActivity, int numberOfTabs) {
        super(fragmentActivity);
        this.tabCount = numberOfTabs;
    }
 
    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return new Tab1Fragment();
            case 1:
                return new Tab2Fragment();
            case 2:
                return new Tab3Fragment();
            case 3:
                return new Tab4Fragment();
            default:
                return null;
        }
    }
 
    @Override
    public int getItemCount() {
        return tabCount;
    }
}Code language: Java (java)

The class is declared as extending the FragmentStateAdapter class, and a constructor is implemented, allowing the number of pages required to be passed to the class when an instance is created. The createFragment() method will be called when a specific page is required. A switch statement is used to identify the page number being requested and to return a corresponding fragment instance. Finally, the getItemCount() method returns the count value passed through when the object instance was created.

Performing the Initialization Tasks

The remaining tasks involve initializing the TabLayout, ViewPager2, and TabPagerAdapter instances and declaring the main activity class as implementing fragment interaction listeners for each of the four tab fragments. Edit the MainActivity.java file so that it reads as follows:

package com.ebookfrenzy.tablayoutdemo;
.
.
import android.net.Uri; 
import com.google.android.material.tabs.TabLayoutMediator;
.
.
public class MainActivity extends AppCompatActivity implements 
	 Tab1Fragment.OnFragmentInteractionListener, 
        Tab2Fragment.OnFragmentInteractionListener, 
        Tab3Fragment.OnFragmentInteractionListener, 
        Tab4Fragment.OnFragmentInteractionListener {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
.
.
        configureTabLayout();
    } 
 
    protected void configureTabLayout() {
 
        for (int i = 0; i < 4; i++) {
            binding.tabLayout.addTab(binding.tabLayout.newTab());
        }
 
        final TabPagerAdapter adapter = new TabPagerAdapter
                (this, binding.tabLayout.getTabCount());
        binding.viewPager.setAdapter(adapter);
 
        new TabLayoutMediator(binding.tabLayout, binding.viewPager,
                (tab, position) -> tab.setText("Tab " + (position + 1) + 
                                " Item")).attach();
    }
 
   @Override
   public void onFragmentInteraction(Uri uri) {
   }
.
.
}Code language: Java (java)

The code begins by creating four tabs and adding them to the TabLayout instance as follows:

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

for (int i = 0; i < 4; i++) {
    binding.tabLayout.addTab(binding.tabLayout.newTab());
}Code language: Java (java)

Next, an instance of the TabPagerAdapter class is created. Note that the code to create the TabPagerAdapter instance passes through the number of tabs assigned to the TabLayout component. The TabPagerAdapter instance is then assigned as the adapter for the ViewPager2 instance:

final TabPagerAdapter adapter = new TabPagerAdapter
        (this, binding.tabLayout.getTabCount());
binding.viewPager.setAdapter(adapter);Code language: Java (java)

Finally, an instance of the TabLayoutMediator class is used to connect the TabLayout with the ViewPager2 object:

new TabLayoutMediator(binding.tabLayout, binding.viewPager,
        (tab, position) -> tab.setText("Tab " + (position + 1) + " Item")).attach();Code language: Java (java)

This class ensures that the TabLayout tabs remain synchronized with the currently selected fragment. This process involves ensuring that the correct text is displayed on each tab. In this case, the text is configured to read “Tab <n> Item” where <n> is replaced by the number of the currently selected tab.

Testing the Application

Compile and run the app on a device or emulator and make sure that selecting a tab causes the corresponding fragment to appear in the content area of the screen:

Figure 46-6

When building the project, you may encounter an error that reads in part:

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules kotlin-stdlibCode language: plaintext (plaintext)

This error is caused by a bug in the Android Studio build toolchain and can be resolved by making the following changes to the build.gradle.kts (Module: app) file:

dependencies {
.
.
    implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
.
.
}Code language: Gradle (gradle)

Customizing the TabLayout

The TabLayout in this example project is configured using fixed mode. This mode works well for a limited number of tabs with short titles. A greater number of tabs or longer titles can quickly become a problem when using fixed mode, as illustrated by Figure 46-7:

Figure 46-7

To fit the tabs into the available display width, the TabLayout has used multiple lines of text. Even so, the second line is truncated, making it impossible to see the full title. The best solution to this problem is to switch the TabLayout to scrollable mode. In this mode, the titles appear in full-length, single-line format allowing the user to swipe to scroll horizontally through the available items, as demonstrated in Figure 46-8:

Figure 46-8

To switch a TabLayout to scrollable mode, change the app:tabMode property in the activity_main.xml layout resource file from “fixed” to “scrollable”:

<android.support.design.widget.TabLayout
    android:id="@+id/tabLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tabMode="scrollable"
    app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout>Code language: HTML, XML (xml)

When in fixed mode, the TabLayout may be configured to control how the tab items are displayed to take up the available space on the screen. This is controlled via the app:tabGravity property, the results of which are more noticeable on wider displays such as tablets in landscape orientation. When set to “fill”, for example, the items will be distributed evenly across the width of the TabLayout, as shown in Figure 46-9:

 

 

Get the Updated Book

You are reading a sample chapter from an old edition of the Android Studio Essentials – Java Edition book.

Purchase the fully updated Android Studio Jellyfish – Java Edition of this book in eBook or Print format.

The full book contains 92 chapters and over 830 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Figure 46-9

Changing the property value to “center” will cause the items to be positioned relative to the center of the tab bar:

Figure 46-10

Summary

TabLayout is one of the components introduced in the Android material design implementation. The purpose of the TabLayout component is to present a series of tab items that display different content to the user when selected. The tab items can display text, images, or a combination. When combined with the ViewPager2 class and fragments, tab layouts can be created relatively easily, with each tab item selection displaying a different fragment.


Categories ,