Using Android TabLayout and ViewPager Tutorial
Today we’re gonna learn about how to create an Android tab application using TabLayout and ViewPager combined together, and like always we will write it in Kotlin language! So first of all, you have to know about TabLayout and ViewPager are.
- TabLayout is a layout from Android Support Design and it is announced by Google back in 2014 as part of Material Design. So basically TabLayout will provide a horizontal layout to display tabs. It can be integrated together with ViewPager to display swipeable fragments between tabs.
- ViewPager is a layout manager which mostly used with Android Fragment which allows users to navigate between fragments using a swipe gesture.
What the app will look like?
The app will look like this after you finish this tutorial:
What will you learn?
- Creating an Android Tab application using a combination of TabLayout and ViewPager
- Displaying Fragment inside ViewPager
- Creating a different kind of tabs from TabLayout
- Building a simple Android tab application using Kotlin programming language
Note: In this post, I used Android Studio 3.2.1, make sure you use the latest Android Studio, or if you already install it, be sure to check the latest update. The Kotlin version that I used is Kotlin 1.3.11.
Getting Started
Open your Android Studio and choose to Start a new Android Studio Project. After that set the Application Name SimpleTabLayout and don’t forget to check the Include Kotlin Support checkbox. After that give the Activity Name MainActivity and wait until the Android Studio finishes preparing your project.
Create three new packages inside your project directory named activity, adapter, and fragment. Move the MainActivity.kt file inside the activity package like this:
Open your app/build.gradle file, add the implementation of Android Support Design implementation 'com.android.support:design:28.0.0'
inside the dependencies block code like this:
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
Next, open the res/values/colors.xml and write this code to give the modify the color of your application, but you can use another color that you like.
<?xml version="1.0" encoding="utf-8"?> <resources> <color name="colorPrimary">#c40551</color> <color name="colorPrimaryDark">#97053f</color> <color name="colorAccent">#fff495</color> </resources>
Creating Adapter
Just like RecyclerView, a ViewPager also needs the adapter to manage its data, in this case, it manages Fragments. Create a new Kotlin class named TabPagerAdapter.kt inside adapter package:
package com.thesimplycoder.simpletablayout.adapter import android.os.Bundle import android.support.v4.app.Fragment import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentPagerAdapter class TabPagerAdapter : FragmentPagerAdapter { private val tabFragmentList = ArrayList<Fragment>() private val tabTitleList = ArrayList<String>() constructor(fragmentManager: FragmentManager) : super(fragmentManager) override fun getItem(position: Int): Fragment { // set argument val args = Bundle() args.putString("arg_title", tabTitleList.get(position)) // init fragment val fragment = tabFragmentList.get(position) fragment.arguments = args return fragment } override fun getCount(): Int { return tabFragmentList.size } override fun getPageTitle(position: Int): CharSequence? { return tabTitleList.get(position) } fun addFragment(fragment: Fragment, title: String) { tabFragmentList.add(fragment) tabTitleList.add(title) } }
The adapter will have to extend the FragmentPagerAdapter which is required for ViewPager to manage the fragments inside it. There are three override methods from this class:
getItem(position: Int)
is a method that returns a specific fragment for a specific position.getCount()
is a method that returns a total size of the fragment. ViewPager will display them afterward.getPageTitle(position: Int)
is a method that returns a title for each position. The Android TabLayout will display this title on each of tabs inside TabLayout.
Inside method getItem(position: Int)
I also plan to assign an argument to each of fragment to display the title later.
Creating Fragment
Create a new layout resource file inside the res/layout directory and name it fragment_tab.xml, write this inside it:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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"> <TextView android:id="@+id/tvTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textSize="24sp" android:textAllCaps="true" android:textStyle="bold" tools:text="Title"/> </RelativeLayout>
After that, create a new Kotlin class named TabFragment.kt inside fragment package:
package com.thesimplycoder.simpletablayout.fragment import android.os.Bundle import android.support.v4.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.thesimplycoder.simpletablayout.R import kotlinx.android.synthetic.main.fragment_tab.* class TabFragment : Fragment() { companion object { fun newInstance(title: String): Fragment { // set argument val args = Bundle() args.putString("arg_title", title) // init fragment val fragment = TabFragment() fragment.arguments = args return fragment } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_tab, container, false) return view } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // get title argument val title = arguments?.getString("arg_title") // display the title to TextView tvTitle.text = title } }
That code above will simply a fragment class that will display a page title into a single TextView. The title comes from the argument that has been sent from our TabPagerAdapter class before. Make sense right!
Creating Fixed Tab
The first type of Android TabLayout is a fixed tab, so we will create it first. So, create a new layout resource file inside the res/layout directory and name it activity_fixed_tab.xml like this:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/colorPrimary" app:tabIndicatorColor="@color/colorAccent" app:tabIndicatorHeight="4dp" app:tabMode="fixed" app:tabGravity="fill" app:tabSelectedTextColor="@android:color/white" app:tabInlineLabel="true" app:tabTextColor="#e4e4e4" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#dedede" /> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
So basically you set the tab mode attribute app:tabMode="fixed"
to make the TabLayout to be a fixed tab. Next, create a new Kotlin class named FixedTabActivity.kt inside activity package.
package com.thesimplycoder.simpletablayout.activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem import com.thesimplycoder.simpletablayout.R import com.thesimplycoder.simpletablayout.adapter.TabPagerAdapter import com.thesimplycoder.simpletablayout.fragment.TabFragment import kotlinx.android.synthetic.main.activity_fixed_tab.* class FixedTabActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_fixed_tab) setTitle("Fixed Tab") supportActionBar?.setDisplayHomeAsUpEnabled(true) // init ViewPager initViewPager() // set ViewPager into TabLayout tabLayout.setupWithViewPager(viewPager) } private fun initViewPager() { // init the adapter val adapter = TabPagerAdapter(supportFragmentManager) // init the fragments adapter.addFragment(TabFragment(), "Contact") adapter.addFragment(TabFragment(), "Phone") adapter.addFragment(TabFragment(), "Message") // set adapter to ViewPager viewPager.adapter = adapter } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { onBackPressed() return true } } return super.onOptionsItemSelected(item) } }
So simple right? It will display the activity of a fixed tab like this:
Creating Fixed-Centered Tab
Create another resource file named activity_fixed_centered_tab.xml. For the fixed-centered tab, basically similar with fixed tab before, but you set it the tab gravity attribute app:tabGravity="center"
like this:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/colorPrimary" app:tabIndicatorColor="@color/colorAccent" app:tabIndicatorHeight="4dp" app:tabMode="fixed" app:tabGravity="center" app:tabSelectedTextColor="@android:color/white" app:tabInlineLabel="true" app:tabTextColor="#e4e4e4" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#dedede" /> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Add a new Kotlin class file named FixedCenteredTabActivity.kt like this:
package com.thesimplycoder.simpletablayout.activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem import com.thesimplycoder.simpletablayout.R import com.thesimplycoder.simpletablayout.adapter.TabPagerAdapter import com.thesimplycoder.simpletablayout.fragment.TabFragment import kotlinx.android.synthetic.main.activity_fixed_centered_tab.* class FixedCenteredTabActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_fixed_centered_tab) setTitle("Fixed-Centered Tab") supportActionBar?.setDisplayHomeAsUpEnabled(true) // init ViewPager initViewPager() // set ViewPager into TabLayout tabLayout.setupWithViewPager(viewPager) } private fun initViewPager() { // init the adapter val adapter = TabPagerAdapter(supportFragmentManager) // init the fragments adapter.addFragment(TabFragment(), "Contact") adapter.addFragment(TabFragment(), "Phone") // set adapter to ViewPager viewPager.adapter = adapter } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { onBackPressed() return true } } return super.onOptionsItemSelected(item) } }
Yeah it’s the same as fixed but the difference is the gravity is centered off the page like this:
Creating Scrollable Tab
Create another resource file named activity_scrollable_tab.xml. The difference is just you set it the tab mode attribute app:tabMode="scrollable"
like this:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.TabLayout android:id="@+id/tabLayout" android:layout_width="match_parent" android:layout_height="50dp" android:background="@color/colorPrimary" app:tabIndicatorColor="@color/colorAccent" app:tabIndicatorHeight="4dp" app:tabMode="scrollable" app:tabGravity="fill" app:tabSelectedTextColor="@android:color/white" app:tabInlineLabel="true" app:tabTextColor="#e4e4e4" /> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:background="#dedede" /> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Next, create another class named ScrollableTabActivity.kt that will display the scrollable tab. I add several more fragment here so you could see that the tab now is scrollable.
package com.thesimplycoder.simpletablayout.activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem import com.thesimplycoder.simpletablayout.R import com.thesimplycoder.simpletablayout.adapter.TabPagerAdapter import com.thesimplycoder.simpletablayout.fragment.TabFragment import kotlinx.android.synthetic.main.activity_scrollable_tab.* class ScrollableTabActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_scrollable_tab) setTitle("Scrollable Tab") supportActionBar?.setDisplayHomeAsUpEnabled(true) // init ViewPager initViewPager() // set ViewPager into TabLayout tabLayout.setupWithViewPager(viewPager) } private fun initViewPager() { // init the adapter val adapter = TabPagerAdapter(supportFragmentManager) // init the fragments adapter.addFragment(TabFragment(), "Fantasy") adapter.addFragment(TabFragment(), "Romance") adapter.addFragment(TabFragment(), "Sci-fi") adapter.addFragment(TabFragment(), "Horror") adapter.addFragment(TabFragment(), "Action") adapter.addFragment(TabFragment(), "Comedy") adapter.addFragment(TabFragment(), "Drama") adapter.addFragment(TabFragment(), "Western") adapter.addFragment(TabFragment(), "TV Series") // set adapter to ViewPager viewPager.adapter = adapter } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { onBackPressed() return true } } return super.onOptionsItemSelected(item) } }
The scrollable tab will display like this:
Creating Icon and Text Tab
Add a new class named IconTextTabActivity.kt here:
package com.thesimplycoder.simpletablayout.activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem import com.thesimplycoder.simpletablayout.R import com.thesimplycoder.simpletablayout.adapter.TabPagerAdapter import com.thesimplycoder.simpletablayout.fragment.TabFragment import kotlinx.android.synthetic.main.activity_fixed_tab.* class IconTextTabActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_fixed_tab) setTitle("Icon & Text Tab") supportActionBar?.setDisplayHomeAsUpEnabled(true) // init ViewPager initViewPager() // set ViewPager into TabLayout tabLayout.setupWithViewPager(viewPager) // set icons setTabIcons() } private fun initViewPager() { // init the adapter val adapter = TabPagerAdapter(supportFragmentManager) // init the fragments adapter.addFragment(TabFragment(), "Home") adapter.addFragment(TabFragment(), "Star") adapter.addFragment(TabFragment(), "Loved") // set adapter to ViewPager viewPager.adapter = adapter } private fun setTabIcons() { tabLayout.getTabAt(0)?.setIcon(R.drawable.ic_home) tabLayout.getTabAt(1)?.setIcon(R.drawable.ic_star) tabLayout.getTabAt(2)?.setIcon(R.drawable.ic_loved) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { onBackPressed() return true } } return super.onOptionsItemSelected(item) } }
So I just reuse my existing fixed tab layout for this because it’s the same type of Android TabLayout, only the difference is displaying text with an icon for each tab. Awesome! The appearance will look like this:
Creating PagerTabStrip
Now it is a little bit different kind of tab, a PagerTabStrip is more from ViewPager component, so it will not use a TabLayout to display it. Add a new layout named activity_pagertabstrip.xml.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.PagerTabStrip android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top"/> </android.support.v4.view.ViewPager> </LinearLayout>
For the activity code PagerStripActivity.kt write the following code:
package com.thesimplycoder.simpletablayout.activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.view.MenuItem import com.thesimplycoder.simpletablayout.R import com.thesimplycoder.simpletablayout.adapter.TabPagerAdapter import com.thesimplycoder.simpletablayout.fragment.TabFragment import kotlinx.android.synthetic.main.activity_pagertabstrip.* class PagerStripActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_pagertabstrip) setTitle("PagerStrip") supportActionBar?.setDisplayHomeAsUpEnabled(true) // init ViewPager initViewPager() } private fun initViewPager() { // init the adapter val adapter = TabPagerAdapter(supportFragmentManager) // init the fragments adapter.addFragment(TabFragment(), "Fantasy") adapter.addFragment(TabFragment(), "Romance") adapter.addFragment(TabFragment(), "Sci-fi") adapter.addFragment(TabFragment(), "Horror") adapter.addFragment(TabFragment(), "Action") adapter.addFragment(TabFragment(), "Comedy") adapter.addFragment(TabFragment(), "Drama") adapter.addFragment(TabFragment(), "Western") adapter.addFragment(TabFragment(), "TV Series") // set adapter to ViewPager viewPager.adapter = adapter } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { android.R.id.home -> { onBackPressed() return true } } return super.onOptionsItemSelected(item) } }
It will display a different kind of Android Tab like this picture:
Final Step
The final step is just putting together all of our Android tabs right here. So open the activity_main.xml and replace the entire code to this code:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/tvFixedTab" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:background="?selectableItemBackground" android:clickable="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="Fixed Tab" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#dedede"/> <TextView android:id="@+id/tvFixedCenteredTab" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:background="?selectableItemBackground" android:clickable="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="Fixed-Centered Tab" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#dedede"/> <TextView android:id="@+id/tvScrollableTab" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:background="?selectableItemBackground" android:clickable="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="Scrollable Tab" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#dedede"/> <TextView android:id="@+id/tvIconTextTab" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:background="?selectableItemBackground" android:clickable="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="Icon & Text Tab" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#dedede"/> <TextView android:id="@+id/tvPagerTabStrip" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center_vertical" android:paddingLeft="20dp" android:paddingRight="20dp" android:background="?selectableItemBackground" android:clickable="true" android:textSize="16sp" android:textColor="@android:color/black" android:text="PagerTabStrip" /> <View android:layout_width="match_parent" android:layout_height="1dp" android:background="#dedede"/> </LinearLayout> </ScrollView>
Open the MainActivity.kt file and write this following code:
package com.thesimplycoder.simpletablayout.activity import android.content.Intent import android.support.v7.app.AppCompatActivity import android.os.Bundle import com.thesimplycoder.simpletablayout.R import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) tvFixedTab.setOnClickListener { startActivity(Intent(this, FixedTabActivity::class.java)) } tvFixedCenteredTab.setOnClickListener { startActivity(Intent(this, FixedCenteredTabActivity::class.java)) } tvScrollableTab.setOnClickListener { startActivity(Intent(this, ScrollableTabActivity::class.java)) } tvIconTextTab.setOnClickListener { startActivity(Intent(this, IconTextTabActivity::class.java)) } tvPagerTabStrip.setOnClickListener { startActivity(Intent(this, PagerStripActivity::class.java)) } } }
The last step is to modify the AndroidManifest.xml and assign the new activities that we have created before.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.thesimplycoder.simpletablayout"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".activity.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".activity.FixedTabActivity" /> <activity android:name=".activity.FixedCenteredTabActivity" /> <activity android:name=".activity.ScrollableTabActivity" /> <activity android:name=".activity.IconTextTabActivity" /> <activity android:name=".activity.PagerStripActivity" /> </application> </manifest>
So that’s all! You can now run your app and let’s see how it perform!
Where to go next?
You can download this full code from my GitHub repo here. So In the next post, I will show you how to create an Android Navigation Drawer application, so stay tuned. Be sure to check my other post here about creating a simple Image Gallery application using RecyclerView. Hope you like my post, comment and share it with love!
1 Response
[…] Android TabLayout and ViewPager Tutorial […]