Android & Kotlin

Android ViewPager with Tabs

Pinterest LinkedIn Tumblr

Welcome guys, I help you build a better app, In this post, we’ll learn step by step implementation of Android ViewPager with Tabs layout. Let’s get to work

What is ViewPager?

ViewPager is controlled action swiping between different pages of content. ViewPager gets content from adapter class. These are listed below

PagerAdapter

It’s base class of adapter that provides a generic view for each page to ViewPager.

FragmentPagerAdapter

It fragment represents each page as Fragment. FragmentPagerAdapter keeps each fragment that created in memory. That why its very fast in switch already loaded tab. It the best for use while you using tabs. When you have a large number of fragments then it memory expensive. For solving this problem we used FragmentStatePagerAdapter.

FragmentStatePagerAdapter

It solves the FragmentPagerAdapter problem. It destroys and recreates the fragment as needed only saving state.

In this ViewPager tutorial, we’ll use FragmentPagerAdapter. How to do that let’s see.

Step of implementation ViewPager

  • Project setup and dependencies
  • Prepare activity layout
  • Create Fragment
  • Create a subclass of FragmentPagerAdapter and override below methods
    • getItem()
    • getPageTitle()
    • getCount
  • Open Activity and set ViewPager on TabLayout

Final Outcome

Project setup and dependencies

Let’s move to Android Studio. Now open app build.gradle and add material io dependency

implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'com.google.android.material:material:1.0.0'

Update Color Resource File

Open the color.xml file in res=>value folder and add below line of code

  <string name="tab_text_1">Speed Dial</string>
  <string name="tab_text_2">Recents</string>
  <string name="tab_text_3">Contacts</string>

Prepare layout for ViewPager with Tabs

Before moving the logic part, let’s prepare XML layout. Open the activity layout file and paste below code.

<?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/AppTheme.AppBarOverlay"
      >

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:minHeight="?actionBarSize"
        android:padding="@dimen/appbar_padding"
        android:text="@string/app_name"
        android:textAppearance="@style/TextAppearance.Widget.AppCompat.Toolbar.Title"
        />

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

  <androidx.viewpager.widget.ViewPager
      android:id="@+id/view_pager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"
      />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Create FragmentPagerAdapter

In the src folder, create a subclass of FragmentPagerAdapter named is TabsPagerAdapter. That returns a fragment corresponding to one of the sections/tabs/pages.

For FragmentPagerAdapter implementation, you must override the following method
  • getItem() – In case of FragmentPagerAdapter you need to implement getItem() which returns the fragments associated with each position. See the below code here we have returns SpeedDialFragment, RecentsFragment, ContactsFragment
  • getPageTitle() – It’s return string title for describing each pages.
  • getCount() – Need this method to return of number of pages you have
package com.example.viewpager.ui.main;

import android.content.Context;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.example.viewpager.R;

public class TabsPagerAdapter extends FragmentPagerAdapter {

  @StringRes
  private static final int[] TAB_TITLES =
      new int[] { R.string.tab_text_1, R.string.tab_text_2, R.string.tab_text_3 };
  private final Context mContext;

  public TabsPagerAdapter(Context context, FragmentManager fm) {
    super(fm);
    mContext = context;
  }

  @Override
  public Fragment getItem(int position) {
    switch (position) {
      case 0:
        return SpeedDialFragment.newInstance();
      case 1:
        return RecentsFragment.newInstance();
      case 2:
        return ContactsFragment.newInstance();
      default:
        return null;
    }
  }

  @Nullable
  @Override
  public CharSequence getPageTitle(int position) {
    return mContext.getResources().getString(TAB_TITLES[position]);
  }

  @Override
  public int getCount() {
    // Show 3 total pages.
    return 3;
  }
}

Create fragment that used to display in ViewPager

In this example, we’ll need three fragments, The name of these fragments is SpeedDialFragment, RecentsFragment, ContactsFragment. For demo, I’m creating a single fragment rest fragment you just replicate the first one.

I’m creating only SpeedDialFragment

Create a new fragment named is SpeedDialFragment. Now open layout file of this fragment and paste below code.

<?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:id="@+id/constraintLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

  <TextView
      android:id="@+id/section_label"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="8dp"
      android:layout_marginEnd="16dp"
      android:layout_marginLeft="16dp"
      android:layout_marginRight="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="8dp"
      android:textColor="@color/colorPrimaryDark"
      android:textSize="20sp"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      tools:text="ViewPager Example"
      />
  <ImageView
      android:id="@+id/imageView"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginBottom="64dp"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:src="@drawable/ic_call"
      app:layout_constraintBottom_toTopOf="@+id/section_label"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      />

</androidx.constraintlayout.widget.ConstraintLayout>

Open fragment Java file and paste below code

package com.example.viewpager.ui.main;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
import com.example.viewpager.R;

public class SpeedDialFragment extends Fragment {

  private static final String TAG = "SpeedDial";

  private PageViewModel pageViewModel;

  public SpeedDialFragment() {
    // Required empty public constructor
  }

  /**
   * @return A new instance of fragment SpeedDialFragment.
   */
  public static SpeedDialFragment newInstance() {
    return new SpeedDialFragment();
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    pageViewModel = ViewModelProviders.of(this).get(PageViewModel.class);
    pageViewModel.setIndex(TAG);
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View root = inflater.inflate(R.layout.fragment_main, container, false);
    final TextView textView = root.findViewById(R.id.section_label);
    pageViewModel.getText().observe(this, new Observer<String>() {
      @Override
      public void onChanged(@Nullable String s) {
        textView.setText(s);
      }
    });
    return root;
  }
}

Similarly, you have to create RecentsFragment, ContactsFragment as well

Create ViewModel

In src folder create a ViewPager for implementing MVVM. Google recommends ViewModel for communicating to fragment.

package com.example.viewpager.ui.main;

import androidx.arch.core.util.Function;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Transformations;
import androidx.lifecycle.ViewModel;

public class PageViewModel extends ViewModel {

  private MutableLiveData<String> mTitle = new MutableLiveData<>();

  private LiveData<String> mText = Transformations.map(mTitle, new Function<String, String>() {
    @Override
    public String apply(String input) {
      return "Contact not available in " + input;
    }
  });

  public void setIndex(String index) {
    mTitle.setValue(index);
  }

  public LiveData<String> getText() {
    return mText;
  }
}

Open MainActivity and add below code

Now all logic stuff is done. Now open the MainActivity file and bind the ViewPager and Tabs layout. Finally set the ViewPager on tab layout.

package com.example.viewpager;

import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;
import com.example.viewpager.ui.main.TabsPagerAdapter;

public class MainActivity extends AppCompatActivity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    TabsPagerAdapter tabsPagerAdapter = new TabsPagerAdapter(this, getSupportFragmentManager());

    ViewPager viewPager = findViewById(R.id.view_pager);
    viewPager.setAdapter(tabsPagerAdapter);

    TabLayout tabs = findViewById(R.id.tabs);
    tabs.setupWithViewPager(viewPager);

  }
}

Get Solution Code

Conclusion

Now build the application and run project. You see your app up and run.

4 Comments

  1. so whether we need to create 3.xml, for three fragments each ?
    or only one xml will work if we need different ui’s for all above 3 fragments ?

  2. Emmanuel Umeanor Reply

    Wonderful!! Thank you for the break down.
    It was worth the read.

  3. SAUMYA RAJAN Reply

    Whenever fragment item is changed this is called “SpeedDialFragment.newInstance()” is called, which is creating new object every time is this a good practice? i am new to java and android pls help.

Write A Comment