Custom views at TabLayout with highlighted text style at the selected tab

In this article i will try to explain how to use TabLayout with AppCompatTextView and how to add style to the selected tab view.

I will assume that you are already familiar with creating a ViewPager with FragmentPagerAdapter and connecting it with TabLayout. If you are not familiar with that, there is good explanation of how to setup view pager with TabLayout at this tutorial.

If you want Fragments in the FragmentPagerAdapter to restore their state properly, you can check out my article where i explain step by step how to create the FragmentPagerAdapter.

Extending from TabLayout and compiling the class

class MyTabLayout : TabLayout {
private val mTitles: MutableList<String> = arrayListOf()
private var mUnselectedTypeFace: Typeface? = null

constructor
(context: Context?) : super(context) {
init(context, null)
}

constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
}

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}

private fun init(context: Context?, attrs: AttributeSet?) {
}
}
All code examples are written in Kotlin using AndroidX libraries packages.

First extend from TabLayout.class, override the constructors and define list variable where we will store the titles for the tabs. After doing this your class should look like as the code example above. The newly added methods and variables are:

  • private val mTitle is the variable where we will store the titles.
  • private fun init(context: Context?, attrs: AttributeSet?) is a method where we will initialise the TabLayout view.
  • private var mUnselectedTypeFace is the typeface which will be the typeface for the default(unselected) tab view.

Creating the custom view for the tab

Let’s add a method that creates AppCompatTextView wihch we will use as the custom view for the tab.

private fun createCustomFontTextViewForTab(): AppCompatTextView {
val customFontTextView = AppCompatTextView(context)
customFontTextView.gravity = Gravity.CENTER
TextViewCompat.setTextAppearance(customFontTextView, R.style.MyTitleStyle)
return customFontTextView
}

The method it is pretty straightforward. One thing that i want to highlight in the method is that you can use any style for the view. Setting the the style of the AppCompatTextView is done with TextViewCompat.setTextAppearance from the support compat library. The style that i use in this tutorial is:

<style name="MyTitleStyle" parent="android:Widget.TextView">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center_vertical</item>
<item name="android:textSize">20sp</item>
<item name="android:textColor">@color/primaryTextColor87</item>
</style>
In this article i am using AppCompatTextView as the custom view for the tab, but you can use any view that you like. If you use your own custom view, the code in this tutorial will not work and you need to change the code accordingly.

Adding a tab indicator

Adding the tab indicator is really simple. We need to create a style in xml and set it at the TabLayout.

<style name=”MyTabLayoutStyle” parent=”Widget.Design.TabLayout”>
<item name=”tabIndicatorColor”>#E97E78</item>
</style>

Above is shown the style of the TabLayout that we will use for this tutorial. At tabIndicatorColor attribute we define the color for the indicator below the selected tab. I will not go into details about the attributes at the style of TabLayout in this article. You can read more about the attributes at the official documentation.

The style of the TabLayout in defined in xml with style=”@style/MyTabLayoutStyle”. The TabLayout view in xml should look like this:

<solution.digital.creative.com.passwordmanager.app.article_tab_layout.MyTabLayout
android:id="@+id/tab_layout"
style="@style/MyTabLayoutStyle"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorWhite"
/>

Creating methods for adding the custom views at the tabs

Next thing that we should do is to create a method that adds the custom views to the tabs. To create the view for the tab we will use the previously created method createCustomFontTextViewForTab.

At the code block below is the method that adds the views at the tab layout.

private fun addViews() {
for (i in 0 until tabCount) {
val tab = getTabAt(i)
if (tab != null) {
val customFontTextView = createCustomFontTextViewForTab()
if (i == 0) {
if (mUnselectedTypeFace == null) {
mUnselectedTypeFace = customFontTextView.typeface
}
customFontTextView.setTypeface(customFontTextView.typeface, Typeface.BOLD)
}
tab.customView = customFontTextView
}
}
}
Code looks a bit messy because of the format at the editor. Don’t worry i’ll explain each line of code. 😃

This method iterates trough all the tabs and for each tab a custom view is created at this line val customFontTextView = createCustomFontTextViewForTab().

Now that we have the custom view which is in the default(unselected) state, we will store it’s typeface at the mUnselectedTypeFace variable. We save the typeface only once at the first(0 index) tab.

Because the first tab is the one that is selected by default, we set it’s typeface to Bold with customFontTextView.setTypeface(customFontTextView.typeface, Typeface.BOLD).

At the end we just set the newly created view as the custom view for the tab at tab.customView = customFontTextView.

Adding the views at the TabLayout

So far we have created methods for adding the views. To add the views we need to override the method setupWithViewPager (viewPager: ViewPager?, autoRefresh: Boolean) and call addViews() after the super method. How this should looks like is shown at the following code block.

override fun setupWithViewPager(viewPager: ViewPager?, autoRefresh: Boolean) {
super.setupWithViewPager(viewPager, autoRefresh)
addViews()
}
super.setupWithViewPager(viewPager, autoRefresh) is the method where the tabs are actually created that is why we call addViews() after him.

Setting the titles at the tabs

At this time you can run your application and the tabs will be shown but there would be no text at the tabs.

I apologise because of the bad quality of the animated images due to a resizing. There is a link at the captions of the images where you can see it full resolution.
You can see in full resolution here.
/**
* This method sets the titles at the custom view at tab.
*/
fun setTitlesAtTabs(titles: List<String>?) {
if (titles == null || titles.size < tabCount) {
return
}
if (this.mTitles.size > 0) {
this.mTitles.clear()
}
this.mTitles.addAll(titles)
for (i in 0 until tabCount) {
val tab = getTabAt(i)
if (tab != null) {
val view = tab.customView
if (view is AppCompatTextView) {
view.text = mTitles[i]
}
}
}
}

This method is straightforward so i won’t exlpain each line of code. It simply iterates through all the tabs and adds a title to the custom view.

Ok we’ve done a lot of coding 😫. Now let’s see the result of the hard work.

You can see in full resolution here.

As you can see the first tab is bolded but when we switch to other tabs the text on the selected tab is not bolded. We don’t want that behaviour so let’s fix that.

Changing the style of the current selected custom tab view

The only thing that is left for us is to change the font appearance of the tab for the selected and unselected state. We can do that by adding TabLayout.OnTabSelectedListener at which we get callbacks when the tab is selected and unselected.

private fun addOnTabSelectedListener() {
addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: Tab?) {
}

override fun onTabUnselected(tab: Tab?) {
val view = tab?.customView
if (view is AppCompatTextView) {
view.typeface = mUnselectedTypeFace
}
}

override fun onTabSelected(tab: Tab?) {
val view = tab?.customView
if (view is AppCompatTextView) {
view.setTypeface(view.typeface, Typeface.BOLD)
}
}
})
}

The method above adds tab listener for at the TabLayout. It has three overriden methods:

  • onTabReselected — Fired when the tab that that is currently selected it is selected again. We don’t need to override this method.
  • onTabUnselected — This method is fired when the tab is unselected. In this method we set the typeface of the TextView to it’s default state.
  • onTabSelected — This method is fired when a new tab is selected. Here we set the previous typeface of the view and we add bolded style.
You can see in full resolution here.

We need to call this method at the init method create previously. Your init method should look like this:

private fun init(context: Context?, attrs: AttributeSet?) {
addOnTabSelectedListener()
}

If you want you can view all the code snippets that i have used for this tutorial here.

Conclusion

Almost every app out there uses tabs at some screen. The preffered way to create the tabs is with TabLayout. 
I hope that in this article I have explained how to use TabLayout with custom tabs, that switch between their selected and unselected state.

If you think this article was helpful to you, be sure to give it 👏👏👏.
 Also, you can follow me on
Medium