Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android:orientation="vertical" does not work for TabWidget

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>
like image 305
aiboman Avatar asked Nov 10 '10 23:11

aiboman


People also ask

How do I change my Android screen to vertical?

Set in this android:screenOrientation="portrait" in your activity in manifest . Show activity on this post. android:configChanges="" help us to prevent onpause() and OnResume() method when we made rotation.

What is Android orientation horizontal?

"horizontal" allows your elements within the Layout to be placed beside each other where as "vertical" will stack them.


3 Answers

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

like image 36
Romain Guy Avatar answered Oct 06 '22 18:10

Romain Guy


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

like image 160
Timores Avatar answered Oct 06 '22 17:10

Timores


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.

like image 44
ATom Avatar answered Oct 06 '22 16:10

ATom