12

My app has a tabhost with four tabs, and now I'm trying to make nice layouts for landscape mode. In order to make use of additional horizontal space I want to put TabWidget at the right side of the screen, and of cource all tabs must be one under another (like in a column). But when switching to landscape mode all tabs get aligned in a row, it looks quite ugly. How to fix that?

Screenshot

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/tabhost"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <LinearLayout

        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        >

        <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            android:layout_weight="1">

            <include layout="@layout/filter_wizard"/>
            <include layout="@layout/filter_wizard"/>
            <include layout="@layout/filter_wizard"/>
            <include layout="@layout/filter_wizard"/>

            </FrameLayout>

             <TabWidget
            android:background="#75ffffff"
            android:id="@android:id/tabs"
            android:layout_width="wrap_content" android:orientation="vertical" 
            android:layout_height="fill_parent" android:layout_weight="0" />



    </LinearLayout>
</TabHost>
Emre Erkan
  • 8,433
  • 3
  • 48
  • 53
aiboman
  • 131
  • 1
  • 2
  • 5
  • I've actually tried getting vertical tabs to work using the tabhost/tabwidget. I never got it to work. you'll probably have to roll your own tab view to do it. – Falmarri Nov 10 '10 at 23:36
  • I agree with Falmarri. This needs to be done in your own custom layout. TabHost isn't very nice when trying to customize it to look good for both portrait and landscape. – Austyn Mahoney Nov 11 '10 at 01:03
  • Falmarri, Austyn Mahoney, thanks for the explanation. Is there a feasible way to implement own tabwidget? – aiboman Nov 13 '10 at 13:36
  • Do you have a way to publish your project ? –  Jun 23 '11 at 12:20
  • Can you post your solution ?The megaupload link is expired now – Dharmendra Sep 01 '11 at 06:35

4 Answers4

21

This is how I set up the TabHost to display the tabs on the left of the screen, with tabs vertically stacked.

One needs to set up 2 different layouts for the activity, one in portrait ("normal") mode, one in landscape mode. This implies to not use TabActivity.

I copied the layout used by TabActivity into my own project and called it main_view.xml (stored in res/layout). Here it is:

<TabHost xmlns:android="http://schemas.android.com/apk/res/android" 
         android:id="@+id/tabHost"
         android:layout_width="match_parent" 
         android:layout_height="match_parent">
    <LinearLayout android:orientation="vertical"
                  android:layout_width="match_parent" 
                  android:layout_height="match_parent">
        <TabWidget android:id="@android:id/tabs"
                   android:layout_height="wrap_content" 
                   android:layout_width="match_parent"
                   android:layout_weight="0" />
        <FrameLayout android:id="@android:id/tabcontent"
                     android:layout_width="match_parent" 
                     android:layout_height="0dip"
                     android:layout_weight="1"/>
    </LinearLayout>
</TabHost>

One must reuse the Android ids tabs and tabcontent.

In landscape, I changed this by inverting the layout height/width attributes for all controls and setting the orientation of LinearLayout to horizontal (the TabWidget and FrameLayout must be next to each other, horizontally). Here is the result, in res/layout-land, also called main_view.xml:

<TabHost   xmlns:android="http://schemas.android.com/apk/res/android" 
           android:id="@+id/tabHost"
           android:layout_width="match_parent" 
           android:layout_height="match_parent">
    <LinearLayout android:orientation="horizontal"
                  android:layout_width="match_parent" 
                  android:layout_height="match_parent">
        <TabWidget android:id="@android:id/tabs" 
                   android:layout_height="match_parent" 
                   android:layout_width="wrap_content"
                   android:layout_weight="0" />
        <FrameLayout android:id="@android:id/tabcontent"
                     android:layout_height="match_parent" 
                     android:layout_width="0dip"
                     android:layout_weight="1"/>
    </LinearLayout>
</TabHost>

Note that if you want the tabs on the right, you put the TabWidget after the FrameLayout in the XML above.

TabWidget is itself a LinearLayout. Notice that I did not set the orientation in XML. This because TabWidget does it in its own code (yes, it is hard-coded). To counter this, one has to re-set the orientation in code. Here is how I did it in my activity's oncreate

setContentView(R.layout.main_view);

final TabHost tabHost = (TabHost) findViewById(R.id.tabHost);
tabHost.setup();

Resources res = getResources();
Configuration cfg = res.getConfiguration();
boolean hor = cfg.orientation == Configuration.ORIENTATION_LANDSCAPE;

if (hor) {
    TabWidget tw = tabHost.getTabWidget();
    tw.setOrientation(LinearLayout.VERTICAL);
}

As TabHost is created through setContentView, one must call its setup method explicitly.

The usual way to create a tab is to call:

tabHost.addTab(tabHost.newTabSpec("tab name").setIndicator("title", icon).setContent(...));

The setIndicator method, taking a title string and a drawable as parameters, creates a layout that is valid only in portrait mode. One has to create one's own view and give it to setIndicator. It is enough to copy the TabSpec.LabelAndIconIndicatorStrategy.createIndicatorView code:

   private View createIndicatorView(TabHost tabHost, CharSequence label, Drawable icon) {

       LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

       View tabIndicator = inflater.inflate(R.layout.tab_indicator,
               tabHost.getTabWidget(), // tab widget is the parent
               false); // no inflate params

       final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
       tv.setText(label);

       final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
       iconView.setImageDrawable(icon);

       return tabIndicator;
   }

The difference with the original Google code is that the view layout itself, the TextView and ImageView ids are from our own application, not Android internal ids.

For portrait mode, we can reuse the tab_indicator.xml from Android, that we store in res/layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dip"
    android:layout_height="64dip"
    android:layout_weight="1"
    android:layout_marginLeft="-3dip"
    android:layout_marginRight="-3dip"
    android:orientation="vertical"
    android:background="@drawable/tab_indicator">

    <ImageView android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
    />

    <TextView android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        style="?android:attr/tabWidgetStyle"
    />

</RelativeLayout>

Again, this is identical to the original Android XML, except for the ids. For a landscape-friendly version, we need to invert again the layout width and height attributes. Which gives us in res/layout-land:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="64dip"
    android:layout_height="0dip"
    android:layout_weight="1"
    android:layout_marginTop="-3dip"
    android:layout_marginBottom="-3dip"
    android:orientation="vertical"
    android:background="@drawable/tab_indicator">

    <ImageView android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
    />

    <TextView android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        style="?android:attr/tabWidgetStyle"
    />
</RelativeLayout>

(I changed marginLeft and marginRight to marginTop and marginBottom but am not that sure that it is helpful)

These last XML files reference @drawable/tab_indicator, so we need to copy it from Android source code, as well as drawable/tab_selected.9.png, drawable/tab_unselected.9.png, drawable/tab_focus.9.png.

Now creating a tab becomes:

tabHost.addTab(tabHost.newTabSpec(AllTabName)
                .setIndicator(createIndicatorView(tabHost, "tab title", icon)))
                .setContent(this));

EDIT: a demo project is available at: VerticalTabHost on SkyDrive

Timores
  • 14,439
  • 3
  • 46
  • 46
  • i do as your say,but when the screen is horizontal,my get the result is that the screen cannot show tabWidget,only show tabContent – pengwang Apr 02 '11 at 08:33
  • Do you have a way to publish your project ? I can have a look and compare with mine. – Timores Apr 03 '11 at 16:03
  • Thanks for the upload. It looks like my sample of tab_indicator.xml was not displaying correctly. I re-applied the formatting and it looks now complete (sorry about this). The tag was not shown. This part was wrong in your sample in res/layout-land/tab_indicator.xml. I exchanged the layout_width and layout_height attributes (in your project) and the TabWidget is now showing correctly on my emulator. – Timores Apr 05 '11 at 17:31
  • On my emulator, the tabs were showing "reversed", i.e. in landscape view when in portrait and vice-versa. Maybe this is what you wanted. If I remove the android:screenOrientation="landscape" attribute in the manifest, it is displayed "normally". – Timores Apr 05 '11 at 17:34
  • Your link in megauploads is broken can you please Edit your answer and put a source code so that all users can see your post ? I am not able to display tabs vertically it displays only content SO please help me. – Dharmendra Sep 01 '11 at 06:08
  • I've updated the answer to include a link to the sources. Here's also the link: https://skydrive.live.com/?cid=1e771dcd1c382f9c&sc=documents&id=1E771DCD1C382F9C%21284# – Timores Sep 19 '11 at 12:57
  • @Timores I made the xml as said and create custom views for the indicators. My activity is set to landscape only so I can just set tw.setOrientation(LinearLayout.VERTICAL); in the onCreate. But then my TabWidget disappears. on LinearLayout.HORIZONTAL it displays my TabWidget. – Siebe Apr 10 '13 at 13:22
  • @Siebe the comment from Davor in the answer by ATom says the same thing. If you post your code somewhere, I can have a look. – Timores Apr 10 '13 at 14:29
4

TabHost does not support the orientation attribute and tabs can only be used horizontally.

Romain Guy
  • 97,993
  • 18
  • 219
  • 200
3

So, only important think that you must do is this:

getTabWidget().setOrientation(LinearLayout.VERTICAL);

Because TabWidget has hardcoded setOrientation(LinearLayout.HORIZONTAL); in initTabWidget() method. I don't know why.

ATom
  • 15,960
  • 6
  • 46
  • 50
0

Here i have an look alike solution for the Vertical Tab alignment view

Kindly visit this link :-how to change the layout in frame layout on the click event?

Thank You Happpieeee coding

Community
  • 1
  • 1
Rahul Gupta
  • 7
  • 2
  • 8