Android ActionBar Navigation Tabs looks differ in phones and tablets

android, android-actionbar

Solution

The `ActionBar` tabs are created by the `ScrollingTabContainerView`. The `ActionBarView`, the actual `View` for the `ActionBar`, contains a method `ActionBarView.setEmbeddedTabView` which is essentially responsible for the placement of the tabs either below or embedded in the `ActionBar`.

When the `ActionBar` is initialized in `Activity`, it uses the `ActionBarPolicy` to determine when to use the embedded tabs based on a `boolean` value that's defined as `true` in the system's resources, but `false` in portrait mode. So, upon this initialization `ActionBarImpl.setHasEmbeddedTabs` is called to determine where to place the tabs. And also, as the docs state:

...when the screen is wide enough the tabs appear in the action bar alongside the action buttons

So how do I stop it from embedding the tabs?

Seems like you have two choices here and one is clearly, at least to me, better than the other.

You can use reflection to invoke `ActionBarImpl.setHasEmbeddedTabs` and set it to always be `false`. You need to use reflection because `ActionBarImpl` is an internal class and there is no public `ActionBar` method to indicate when to embed tabs.

You can stop using the `ActionBar.Tab` API and switch to an alternative, like Google's `SlidingTabLayout`. This one will require you to use a `ViewPager`, which is usually is a nice plus when using tabs anyway and you may even already be using it.

Here are examples for both:

Using reflection

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final ActionBar actionBar = getActionBar();
    // Implement your tabs
    ...
    try {
        final Class<?> clazz = actionBar.getClass();
        final Method embedTabs = clazz.getDeclaredMethod("setHasEmbeddedTabs", boolean.class);
        embedTabs.invoke(actionBar, false);
    } catch (final Exception ignored) {
        // Nothing to do
    }
}

Reflection results

Switching from the fixed tabs to scrollable tabs

To use Google's `SlidingTabLayout`, you'll need to copy over two classes into your project. The classes you need are:

- SlidingTabLayout

- SlidingTabStrip

An example layout implementing the `SlidingTabLayout` would looks this like:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:orientation="vertical">

    <your_path_to.SlidingTabLayout
          android:id="@+id/slidingTabs"
          android:layout_width="match_parent"
          android:layout_height="wrap_content" />

    <android.support.v4.view.ViewPager
          android:id="@+id/viewPager"
          android:layout_width="match_parent"
          android:layout_height="0dp"
          android:layout_weight="1" />

</LinearLayout> 

After inflating the layout and initializing the `SlidingTabLayout` using `View.findViewById`, you call `SlidingTabLayout.setViewPager` to bind the `ViewPager` to the tabs.

Check out Google's example for a full project

Scrollable tab results

Scrollable tabs are used in several of Google's app, like the Play Store and Play Music. For a short video demonstrating them, if you're unfamiliar, check out the Android design docs on tabs.

Conclusion

In short, I would recommend using scrollable tabs rather than fixed tabs (at least in landscape mode), if you don't want them to be embedded.

Problem

Is it possible for ActionBar Navigation Tabs to be displayed same on both phones and tablets? See images for phone and tablets below: PHONE: TABLET: The Android documentation states "...when the screen is wide enough the tabs appear in the action bar alongside the action buttons...". We don't want them to appear in ActionBar. Any solutions?

Original source