time

How to organize content in tabs with TabLayout and ViewPager in Android

Earlier to make tabs in Android, ActionBar was used. But now with API 21 onwards it has been deprecated. The new Design Support Library has many new UI components like floating action buttons, snackbars and tabs.

The base componets of this tutorial are

  • ViewPager. The primary role of the ViewPager is to allow the user to flip through different pages of information where each page is most typically represented by a layout fragment. The fragments that are associated with the ViewPager are managed by an instance of the FragmentStatePagerAdapter class. At a minimum the pager adapter assigned to a ViewPager must implement two methods. The first, named getCount(), must return the total number of page fragments available to be displayed to the user. The second method, getItem(), is passed a page number and must return the corresponding fragment object ready to be presented to the user.
  • TabLayout is one of the components introduced as part of 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 which can be selected to display different pages to the user.
android_tablayout.png

To start off include following libraries in the dependencies section of your build.gradle file

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:22.2.1'
    compile 'com.android.support:design:22.2.0'
}

We will be using Android Toolbar and TabLayout classes to show tabs, lets remove the ActionBar from layout by using styles. In order to change the theme, go to app - res – values – styles and change the default theme Theme.AppCompat.Light.DarkActionBar to Theme.AppCompat.Light.NoActionBar.

# file styles.xml
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">#3F51B5</item>
        <item name="colorPrimaryDark">#303F9F</item>
        <item name="colorAccent">#FF4081</item>
    </style>
</resources>

Next, to display Android tabs with fragment and ViewPager, lets define simple fragment

# file TabFragment.java

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;

public class TabFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.tab_fragment, container, false);
        Button btnIntroduce = (Button) view.findViewById(R.id.btnIntroduce);
        btnIntroduce.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Fragment", Toast.LENGTH_LONG).show();
            }
        });        

        return view;
    }
}

and layout

# file tab_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Tab 1"
        android:textAppearance="?android:attr/textAppearanceLarge"/>

    <Button
        android:id="@+id/btnIntroduce"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Who I am?"
        android:layout_below="@+id/textView"
        android:layout_centerHorizontal="true"        
        />

</RelativeLayout>

Now that we have all the tab fragments defined, lets define a ViewPager adapter for the swipe tabs feature

# file PagerAdapter.java

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

public class PagerAdapter extends FragmentStatePagerAdapter {
    int numTabs;

    public PagerAdapter(FragmentManager fm, int numTabs) {
        super(fm);
        this.numTabs = numTabs;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                TabFragment tab1 = new TabFragment();
                return tab1;
            case 1:
                TabFragment tab2 = new TabFragment();
                return tab2;
            case 2:
                TabFragment tab3 = new TabFragment();
                return tab3;
            default:
                return null;
        }
    }

    @Override
    public int getCount() {
        return numTabs;
    }
}

Next lets define the layout for main activity where all these tabs would be displayed.

# file activity_main.xml

<RelativeLayout
    android:id="@+id/main_layout"
    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">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:background="?attr/colorPrimary"
        android:elevation="6dp"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/tab_layout"/>

</RelativeLayout>

The remaining tasks involve initializing the TabLayout, ViewPager and PagerAdapter instances. All of these tasks will be performed in the onCreate() method of the MainActivity.java file.

# file MainActivity.java

import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
        tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
        tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
        final PagerAdapter adapter = new PagerAdapter
                (getSupportFragmentManager(), tabLayout.getTabCount());
        viewPager.setAdapter(adapter);
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        tabLayout.setOnTabSelectedListener(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) {}
        });
    }
}

Code on github.

Changing size of tab title

In an effort to fit the tabs into the available display width the TabLayout has used multiple lines of text. Even so, the second line is clearly 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.

To switch a TabLayout to scrollable mode, simply 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/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:tabMode="scrollable"
    app:tabGravity="fill"/>
</android.support.design.widget.AppBarLayout>

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.

Changing text size of tab items

To replace the text size of tabs items create new style in style.xml

<style name="CustomTabText" parent="TextAppearance.Design.Tab">
    <item name="android:textSize">16sp</item>
</style>

Use is in TabLayout like this

<android.support.design.widget.TabLayout
    app:tabTextAppearance="@style/CustomTabText"
    ...
    />

Displaying icon tab items

To replace the text based tabs with icons, modify the onCreate() method in the MainActivity.java file to assign some built-in drawable icons to the tab items

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

    TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
    tabLayout.addTab(tabLayout.newTab().setIcon(
            android.R.drawable.ic_dialog_email));
    tabLayout.addTab(tabLayout.newTab().setIcon(
            android.R.drawable.ic_dialog_dialer));
    tabLayout.addTab(tabLayout.newTab().setIcon(
            android.R.drawable.ic_dialog_map));
    ...
}
comments powered by Disqus