Shared publicly  - 
 
Looks like there is some interest in the tab strip of the new Play Store client. Let's talk about it then, shall we?

The tabbed control itself is the ViewPager from the support library. If you don't use it in your project, seriously, who are you? Don't reinvent the wheel, let +Adam Powell do that for you.

ViewPager comes with two implementations of the tab strip - PagerTitleStrip and PagerTabStrip. The first one is not worth talking about (sorry, Adam). The second one is much nicer, allowing tapping the titles to switch to the relevant tab and exposing APIs to control a couple of visual aspects. We were using PagerTabStrip up until the latest iteration of the store app. That is, until our visual designers wanted a nicer appearance that is more inline with the general direction of where the platform tab indicator is heading.

On the visual side of things, we wanted to:

* Use different colors for full underline and the selected tab indicator.
* Use translucent vertical separators between the tabs.
* Have the left-of-the current tab "peek out" to indicate that there's additional content available.
* More compact display of tabs - instead of having left/right title all the way at the edges (which looked particularly bad on wide tablets), display the title tabs as a "connected" chain.
* Nicer swiping feedback as the user swipes between tabs.

As it turns out, you can write your own tab strip control. Who knew, amirite?

Step 1: make yourself familiar with the source code of the existing components [1]. That always helps.
Step 2: throw some code around and see how it looks like on the screen. At some point, after enough Mountain Dew has been consumed, it starts looking somewhat decent. Rinse and repeat.

Let's talk about the particular implementation details (and no, you can't haz the full source code. What am I, a farmer?)

* Each tab title is a TextView. This provides nice accessibility support. Mark it as focusable and set a background drawable that has your app's assets for pressed and focused state.
* If the content of your ViewPager is static, iterate over all page titles and create a TextView for each one. Set a click listener on each text to call ViewPager.setCurrentItem.
* Add a global layout listener and call scrollToChild with ViewPager.getCurrentItem. Don't forget to remove that listener after you're done with the initial scroll. The intent here is to respect the initial tab selection and scroll the selected tab into view.
* Implement ViewPager.OnPageChangeListener (somewhere outside your view pager / tab strip).
* In onPageScrollStateChanged remember the current scroll state.
* In onPageSelected if the current scroll state is SCROLL_STATE_IDLE, remember the selected index, set offset (more on this in the next points) to 0 and call invalidate on your tab strip.
* In onPageScrolled remember the selected index and the offset, call invalidate on your tab strip and also call scrollTo on the tab strip to scroll its contents based on the selected index and the offset.
* What's up with all the scrolling, bro?
* You want to keep the scroll state of the tab strip in sync with what the user is doing with the your view pager. If the user swipes right to go to the tab that is currently to the left, you want to start scrolling the title of that tab as well. How much? That depends on your design. Our target is to have some part of that title always peeking in. So you would need to compute the value of the first parameter of scrollTo based on the tab index, relative scroll offset and that extra peeking delta.
* Now about the pretty pixels.
* Designers love assets. You can create a whole bunch of assets for the individual tab - normal state, pressed state, focused state, pressed+focused state, let's throw the selected state in, and hey bro, what about the disabled state? That's nice. But. Take a look at the second screenshot below - the selected underline "slides" between the two tabs. That's not something that you can do with assets set on each individual TextView behind these two tabs. I guess you can do an empty View, set those assets on it and start laying it out dynamically as you slide. Kind of gross.
* So instead, right now we're painting the underlines in code. Canvas.drawRect is your friend. Just don't create the Paint objects every single time in onDraw.
* First layer the thick colored selection underline. This is where you need the index of the selected tab and the relative scroll amount. During scroll, this underline "slides" from one tab to the next (left or right). So you use the scroll amount to interpolate the X coordinate of both the left and the right edge of that underline based on the X coordinates of the two tabs.
* Second layer is the thin transparent black underline that goes across the entire tab strip. Using transparency creates a nice layered effect across the bottom edge of the colored underline.
* Finally, since we're already drawing lines in code, why not draw the vertical separators in code too? These can be done with drawables set on each title TextView, but you have the edge case of the first/last tab. Since we want separators between the tabs, but not on the outside, you'd need to create two sets of assets - one for the tabs that show that separator (say, along the right edge), and one for that special tab that doesn't (in this case, the very last tab). If you use translucent black for these separators, you'll get a nice blend with the press/focus highlight assets set on the tabs - provided that they are translucent too.
* [ADDITION] - you'll need to provide a way to swipe the tabs themselves. Our current solution is to have the tab views live in a horizontal LinearLayout that lives inside a HorizontalScrollView.

So, to summarize. Track the scrolling. Draw translucent lines. Make your designers happy. You know, #pixelpushing  

Oh, almost forgot. Don't ignore edge cases. Such as, say, all the tabs fitting in on the screen - in which case you don't need anything to "peek in" from the left edge.

[1] https://android.googlesource.com/platform/frameworks/support/+/master/v4/java/android/support/v4/view
152
72
Rosinete Maria Cavalcanti's profile photogonza her's profile photoHamid Malek's profile photoRupan Roy's profile photo
23 comments
 
You guys really have a way of posting those guides right after I've finished my implementation!
 
Thanks for sharing! Looking forward to someone possibly writing their own. +Maria Neumayer any chance you can share your implementation?
 
Mine doesn't have tabs that you can swipe (they're completely on the screen), so it's mostly about the moving tab indicator, which is just done in onPageScrolled as +Kirill Grouchnikov described.
 
Hm, I'd still be interested in that component (maybe a snippet)?  I have an implementation of the PagerTabStrip that handles the offset, so the two remaining components are the moving underline indicator and the full strip scrolling based on onPageScrolled().
 
Yup, the TabPageIndicator is what I started off of.  Implementing the offset was fairly easy but I just don't have the time to dive into the underline/scroll behavior!
 
nice post, and nice pictures... i see a 4g on your screenshot, but wasn't the Nexus4 capable of 4g? it is a new device??
 
oh yes, right... I'm out of battery now... :D
Gnex has 4g, except in my stupid country where carriers are joking with us... :D thanks again for the post, i like explanation without code, helps people use their own brains :D
 
that one looks good. but i prefer the new smaller bar. 
 
Great overview, thanks for posting this.
 
While this control's really nice, I wish something like this was part of the system and only modified with OS updates. IMHO, we've got too much UI fragmentation, i.e. too many (custom) controls for the same purposes. Every app works completely differt. We've got dropdowns and tabs in the Action Bar, there's the old Tab control, there are different ViewPagerIndicator variants, there are ViewPagers without any indicator, ...
It's similar with list item actions. There's the old popup menu, contxtual Action bar, dropdowns like in the music player, icon popups like in contacts, and much more.
Lots oft reinventing the wheel, and the user has to get used to every single app where at least some basic behavior should be the same.
 
this is awesome... no clue what any of it means yet... but hopefully i'll get it all soaked in haha
 
Would you recommend using the 'fixed tabs' instead of the 'scrollable tabs'? I know they're both scrollable, but the design guidelines seem to advise against using this type of tabs with 4 or more items: "Fixed tabs are displayed with equal width, based on the width of the widest tab label. If there is insufficient room to display all tabs, the tab labels themselves will be scrollable. For this reason, fixed tabs are best suited for displaying 3 or fewer tabs."
 
+Kirill Grouchnikov  
Hey, How can I go back from an Activity that has been instantiated, to a tab in a view pager from which it has been instantiated.
 
I can not change the color of text in tabs. I can not tell where android states: textColor. thank you very much
 
is it possible to change text size of tab texts?
 
Hey! can you tell me how to implement the TestAdapter class.
ViewPager pager = null;
try {
pager = (ViewPager) findViewById(R.id.tabs);
pager.setAdapter(new TestAdapter(getSupportFragmentManager()));
} catch (NullPointerException e) {
e.printStackTrace();
}
Add a comment...