Android TabLayout and ViewPager

Filed Under: Android

In this tutorial we’ll be implementing a ViewPager under the TabLayout that we’d already implemented in this tutorial.

Android TabLayout ViewPager Overview

ViewPagers are used to swipe through pages of data. It’s generally used in conjunction with fragments.
Let’s modify our layout from the previous tutorial as below.

activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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"
    android:fitsSystemWindows="true"
    tools:context="com.journaldev.tablayoutviewpager.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.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/AppTheme.PopupOverlay" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            style="@style/MyStyle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabGravity="fill"
            app:tabMode="fixed" />

    </android.support.design.widget.AppBarLayout>


    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />


    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

Before we add up our ViewPager in the MainActivity, let’s set up it’s adapter.


public class ViewPagerAdapter extends FragmentPagerAdapter {

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;
        if (position == 0)
        {
            fragment = new FragmentA();
        }
        else if (position == 1)
        {
            fragment = new FragmentB();
        }
        else if (position == 2)
        {
            fragment = new FragmentC();
        }
        return fragment;
    }

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

    @Override
    public CharSequence getPageTitle(int position) {
        String title = null;
        if (position == 0)
        {
            title = "Tab-1";
        }
        else if (position == 1)
        {
            title = "Tab-2";
        }
        else if (position == 2)
        {
            title = "Tab-3";
        }
        return title;
    }
}

The above ViewPagerAdapter extends the FragmentPagerAdapter. It invokes three Fragments, one for each of its pages. Each of the fragments holds a ListView as shown below

fragment_list.xml


<?xml version="1.0" encoding="utf-8"?>

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/list"/>

The FragmentA(/B/C).java is given below:


public class FragmentA extends Fragment {


    ListView list;

    public FragmentA() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment, container, false);

        list = (ListView) view.findViewById(R.id.list);
        ArrayList stringList= new ArrayList();

        stringList.add("Item 1A");
        stringList.add("Item 1B");
        stringList.add("Item 1C");
        stringList.add("Item 1D");
        stringList.add("Item 1E");
        stringList.add("Item 1F");
        stringList.add("Item 1G");
        stringList.add("Item 1H");
        stringList.add("Item 1I");
        stringList.add("Item 1J");
        stringList.add("Item 1K");
        stringList.add("Item 1L");
        stringList.add("Item 1M");
        stringList.add("Item 1N");
        stringList.add("Item 1O");
        stringList.add("Item 1P");
        stringList.add("Item 1Q");
        stringList.add("Item 1R");
        stringList.add("Item 1S");
        stringList.add("Item 1T");
        stringList.add("Item 1U");
        stringList.add("Item 1V");
        stringList.add("Item 1W");
        stringList.add("Item 1X");
        stringList.add("Item 1Y");
        stringList.add("Item 1Z");

        CustomAdapter adapter = new CustomAdapter(stringList,getActivity());
        list.setAdapter(adapter);

        return view;
    }
}

The CustomAdapter.java class for the above ListView is:


public class CustomAdapter extends ArrayAdapter {

    private ArrayList dataSet;
    Context mContext;

    // View lookup cache
    private static class ViewHolder {
        TextView txtName;

    }

    public CustomAdapter(ArrayList data, Context context) {
        super(context, R.layout.row_item, data);
        this.dataSet = data;
        this.mContext = context;

    }

    @Nullable
    @Override
    public String getItem(int position) {
        return dataSet.get(position);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder; // view lookup cache stored in tag

        if (convertView == null) {

            viewHolder = new ViewHolder();
            LayoutInflater inflater = LayoutInflater.from(getContext());
            convertView = inflater.inflate(R.layout.row_item, parent, false);
            viewHolder.txtName = (TextView) convertView.findViewById(R.id.name);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.txtName.setText(getItem(position));
        // Return the completed view to render on screen
        return convertView;
    }
}

The MainActivity.java class is given below


public class MainActivity extends AppCompatActivity {

    TabLayout tabLayout;
    ViewPager viewPager;
    ViewPagerAdapter viewPagerAdapter;

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

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        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();
            }
        });

        viewPager = (ViewPager) findViewById(R.id.viewPager);
        viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(viewPagerAdapter);
        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);
    }

}

In the above code setupWithViewPager() is used to join the TabLayout with the ViewPager.
The getPageTitle() method in the FragmentPagerAdapter is used to set the title of each of the Tabs. Let’s look at output when the above code is run

android tab layout view pager issue

Question : Why isn’t the ToolBar scrolling as per the scrollFlags set?

It’s due to the ListView. The CoordinatorLayout doesn’t support the ListView(it’s not a part of Material Design) and it’s scrolling gestures. Hence it’s recommended to use RecyclerView instead.

Note: Fragments that belong to a CoordinatorLayout activity need to use NestedScrollView or RecyclerView as parent to allow the scrolling gestures to work correctly.

Before we replace our ListView implementation in the application, let’s wrap the current fragment’s layout with a NestedScrollView as shown below.

fragment_list.xml


<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ListView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</android.support.v4.widget.NestedScrollView>

Let’s see how the application behaves now:

android tablayout viewpager issue

Whoops, the scrolling is fixed but the ListView is displaying only one row now. Hence a ListView should not be used with our Material Design view types. Let’s fix the application now.

Android TabLayout ViewPager Project Structure

android TabLayout ViewPager project

Android TabLayout ViewPager Example Code

The activity_main.xml, MainActivity.java and ViewPagerAdapter.java classes are unchanged. Let’s look at the Fragments now.

The layout of the fragments is given below.

fragment.xml


<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android" />

The FragmentA(/B/C).java is given below


package com.journaldev.tablayoutviewpager;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentA extends Fragment {

    RecyclerView recyclerView;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(
                R.layout.fragment, container, false);
        return rootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        String[] items = getResources().getStringArray(R.array.tab_A);
        RecyclerViewAdapter adapter = new RecyclerViewAdapter(items);
        recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

    }
}

We’ve shifted the data to be displayed into the strings.xml file.
It’s defined there as


<resources>
    <string name="app_name">TabLayoutViewPager</string>
    <string name="action_settings">Settings</string>

    <string-array name="tab_A">
        <item>Item 1A</item>
        <item>Item 1B</item>
    </string-array>

    <string-array name="tab_B">
        <item>Item 2A</item>
    </string-array>
</resources>

Note: We’ve optimised our fragment code logic such that it populates the adapter and displays it once the view is created.

The RecyclerViewAdapter.java has a string array as the argument.
The code for it is given below.


public class RecyclerViewAdapter extends RecyclerView.Adapter {

    String[] items;

    public RecyclerViewAdapter(String[] items) {
        this.items = items;
    }

    @Override
    public TextItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_view_list_item, parent, false);
        return new TextItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(TextItemViewHolder holder, int position) {
        holder.bind(items[position]);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemCount() {
        return items.length;
    }
}

In the above code we’ve added a custom RecyclerViewHolder class that has a layout similar to list items.

The TextItemViewHolder.java class is given below.


public class TextItemViewHolder extends RecyclerView.ViewHolder {
    private TextView textView;


    public TextItemViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.list_item);
    }

    public void bind(String text) {
        textView.setText(text);
    }

}

The layout for the above custom ViewHolder is:

recycler_view_list_item.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/list_item"
        android:textSize="18sp"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingRight="8dp"
        android:paddingLeft="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <View
        android:id="@+id/separator"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#858585" />
</LinearLayout>

The output of the application in action is given below
android tablayout viewpager output

The layout structure resembles that of the WhatsApp Application. To make it more similar do the following changes:

  • Import and add the two menu icon drawables
  • Inflate them in the MainActivity.java in the onCreateOptionsMenu()
  • Change the colorPrimary and colorPrimaryDark to #00897B and #00796B respectively

To inflate the menu layout add the following method in the MainActivity.java.


@Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_main, menu);

        return super.onCreateOptionsMenu(menu);
    }

The menu_main.xml looks like this:


<menu 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"
    tools:context="com.journaldev.tablayoutviewpager.MainActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />

    <item
        android:id="@+id/action_search"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        android:icon="@drawable/search"
        app:showAsAction="ifRoom" />

    <item
        android:id="@+id/action_add"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        android:icon="@drawable/add"
        app:showAsAction="ifRoom" />
</menu>

On doing the above changes you’ll end up with something like this
android tablayout viewpager similar whatsapp

This brings an end to this tutorial. You can download the Android TabLayoutViewPager Project from the below link.

Comments

  1. Giridhar says:

    public class RecyclerViewAdapter extends RecyclerView.Adapter {

  2. rajat nigam says:

    hey bro….thanku for this tutorial.
    i want to know that is it possible to parse json data for tab names?
    plz help me….
    thanks in advance.

    1. Anupam says:

      Parsing JSON is pure Java code. Use getString() or the other alternatives depending on how your JSON is structured.

  3. lahiru deeptha says:

    How to implement countdown timer in a tab fragment while its other tab fragment is having a google map

  4. mehdi hassan says:

    Thanks Bro.
    But i still have problem. Please help me
    i want you to help me create new tab every time i click fab like a new tab in web browser.
    Thanks in Advance.

  5. Sibu Patel says:

    Is it possible to place the tabs vertically?

  6. Andrei says:

    Hey man! Thanks a lot for the tutorial, it was really useful and I couldn’t find anything else to solve my problem until I found this.

    I do have a problem though: instead of a single textview, I have multiple, which need to be populated with different strings from an object, however I haven’t been able to find a proper way to do that. If you could help me with that I’d be in your debt.

    1. Andrei says:

      Nvm, I found the solution to this problem. Thanks again for the great tutorial!

  7. Dhanush says:

    I need tabview with webview

    1. Anupam says:

      Place a inside the fragment.

  8. Charlie says:

    If you add a dialog called from the floating button for add a new item to the list, how can update de fragment??

  9. Abhijeet Gupta says:

    Very useful thank you very much for the tutorial

  10. Pasquale says:

    Could you give the code onclick item ?

  11. amirthapa says:

    how to set a color of title of tab?

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages