Android: customizing the look of Tabs using TabHost & TabWidget

19,310

Solution 1

Rather than trying to customize the widget tabs themselves, here is an alternate approach that I've used successfully on a project that may save you some headaches:

The idea is to use a hidden TabWidget in your layout and control it with a customized LinearLayout containing Buttons. This way, you can more easily customize the buttons to look however you'd like. You'll control the actual TabWidget in your Activity within each button's OnClick.

  1. Create your layout with both the TabWidget and the Buttons:

        <?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">
    
        <RelativeLayout android:orientation="vertical"
            android:layout_width="fill_parent" android:layout_height="fill_parent"
            android:gravity="bottom">
            <TabWidget android:id="@android:id/tabs"
                android:layout_width="fill_parent" android:layout_height="wrap_content"
                android:visibility="gone" />
    
            <LinearLayout android:id="@+id/tabbar"
                android:orientation="horizontal" android:layout_width="fill_parent"
                android:layout_height="wrap_content">
                <Button android:id="@+id/firstButton"
                    android:layout_alignParentTop="true" android:background="@drawable/btn_first_on"
                    android:layout_width="100dp" android:layout_height="43dp"
                    android:clickable="true"></Button>
                <Button android:id="@+id/secondButton"
                    android:layout_alignParentTop="true" android:background="@drawable/btn_second_off"
                    android:layout_height="43dp" android:layout_width="100dp"
                    android:clickable="true"></Button>
                <Button android:id="@+id/thirdButton"
                    android:layout_alignParentTop="true" android:background="@drawable/btn_third_off"
                    android:layout_height="43dp" android:layout_width="100dp"
                    android:clickable="true"></Button>
                <Button android:id="@+id/forthButton"
                    android:layout_alignParentTop="true" android:background="@drawable/btn_forth_off"
                    android:layout_height="43dp" android:layout_width="100dp"
                    android:clickable="true"></Button>
            </LinearLayout>
    
            <FrameLayout android:id="@android:id/tabcontent"
                android:layout_width="fill_parent" android:layout_height="fill_parent"
                android:layout_below="@+id/tabbar" />
    
        </RelativeLayout>
    </TabHost>
    
  2. Set up the onCreate of your activity to handle using the buttons for adjusting the tab views:

        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            // tabs        
            firstButton = (Button) findViewById(R.id.firstButton);
            secondButton = (Button) findViewById(R.id.secondButton);        
            thirdButton = (Button) findViewById(R.id.thirdButton);
            forthButton = (Button) findViewById(R.id.forthButton);
    
            Resources res = getResources(); // Resource object to get Drawables
            final TabHost tabHost = getTabHost();  // The activity TabHost
            TabHost.TabSpec spec;  // Resusable TabSpec for each tab
            Intent intent;  // Reusable Intent for each tab
    
            intent = new Intent().setClass(this, FirstGroupActivity.class);
            spec = tabHost.newTabSpec("first").setIndicator("First").setContent(intent);
            tabHost.addTab(spec);
            intent = new Intent().setClass(this, SecondGroupActivity.class);
            spec = tabHost.newTabSpec("second").setIndicator("Second").setContent(intent);
            tabHost.addTab(spec);   
    
            intent = new Intent().setClass(this, ThirdGroupActivity.class);
            spec = tabHost.newTabSpec("third").setIndicator("Third").setContent(intent);
            tabHost.addTab(spec);
    
    
            intent = new Intent().setClass(this, ForthActivity.class);
            spec = tabHost.newTabSpec("forth").setIndicator("Forth").setContent(intent);
            tabHost.addTab(spec);
    
    
            tabHost.setCurrentTab(0);
    
            firstButton.setOnClickListener(new OnClickListener() {
    
                public void onClick(View v)
                {
                    tabHost.setCurrentTab(0);
                    firstButton.setBackgroundResource(R.drawable.btn_first_on);
                    secondButton.setBackgroundResource(R.drawable.btn_second_off);              
                    thirdButton.setBackgroundResource(R.drawable.btn_third_off);
                    forthButton.setBackgroundResource(R.drawable.btn_forth_off);            
                }
    
            });
    
    
            secondButton.setOnClickListener(new OnClickListener() {
    
                public void onClick(View v)
                {
                    tabHost.setCurrentTab(1);
                    firstButton.setBackgroundResource(R.drawable.btn_first_off);
                    secondButton.setBackgroundResource(R.drawable.btn_second_on);                       
                    thirdButton.setBackgroundResource(R.drawable.btn_third_off);                        
                    forthButton.setBackgroundResource(R.drawable.btn_forth_off);
    
                }
    
            });
    
    
            thirdButton.setOnClickListener(new OnClickListener() {
    
                public void onClick(View v)
                {
                    tabHost.setCurrentTab(3);
                    firstButton.setBackgroundResource(R.drawable.btn_first_off);
                    secondButton.setBackgroundResource(R.drawable.btn_second_off);              
                    thirdButton.setBackgroundResource(R.drawable.btn_third_on);
                    forthButton.setBackgroundResource(R.drawable.btn_forth_off);
    
                }
    
            });
    
    
            forthButton.setOnClickListener(new OnClickListener() {
    
                public void onClick(View v)
                {
                    tabHost.setCurrentTab(4);
                    firstButton.setBackgroundResource(R.drawable.btn_first_off);
                    secondButton.setBackgroundResource(R.drawable.btn_second_off);              
                    thirdButton.setBackgroundResource(R.drawable.btn_third_off);
                    forthButton.setBackgroundResource(R.drawable.btn_forth_on);
    
                }
    
            });
        }
    

As you can see, I'm using drawables for the images of the buttons on and off. Using this technique, you're not limited to the options available when simply just trying to customize the look of the TabWidget's tabs and you can create a completely custom look to your tabs.

Solution 2

1- Use a custom view:

    spec = tabHost.newTabSpec("groups");
    View view = LayoutInflater.from(this).inflate(R.layout.tabwidget_tabs, tabHost.getTabWidget(), false);
    spec.setIndicator(view);
    spec.setContent(intent);

instead of:

    spec = tabHost.newTabSpec("groups").setIndicator("groups", res.getDrawable(R.drawable.ic_tab_groups)).setContent(intent);
    tabHost.addTab(spec);

And then define the view for the tabs in the file tabwidget_tabs.xml (you can define an ImageView before the textView and the textsize):

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/tabsLayout" 
        android:layout_width="wrap_content"
        android:layout_height="34dp"
        android:background="@drawable/tabs_bkgrd"
        android:padding="5dp" 
        android:orientation="vertical">

        <TextView android:id="@+id/tabsText" 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:singleLine="true"
            android:textStyle="bold" 
            android:gravity="center_horizontal"
            android:textSize="14dp" />
    </LinearLayout>

2- It's not possible to use hex triplets to change the background color of the tabs because are drawables not colors. However you can use a selector that changes the drawables. and you can combine this solution with setColorFilter() and android:tint and then you can select the background using hex triplets: How to tint a bitmap

tabs_bkgrd.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Non focused states -->
    <item android:state_focused="false" android:state_selected="false" 
        android:state_pressed="false" android:drawable="@drawable/tab_unselected_shape" />
    <item android:state_focused="false" android:state_selected="true" 
        android:state_pressed="false" android:drawable="@drawable/tab_selected_shape" /> 

    <!-- Focused states -->
    <item android:state_focused="true" android:state_selected="false" 
        android:state_pressed="false" android:drawable="@drawable/tab_focused_shape" />
    <item android:state_focused="true" android:state_selected="true" 
        android:state_pressed="false" android:drawable="@drawable/tab_focused_shape" /> 

    <!-- Pressed -->
    <item android:state_pressed="true" android:drawable="@drawable/tab_pressed_shape" /> 

    </selector>

You can define a color or a shape, tab_selected_shape.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
                android:shape="rectangle">
            <gradient android:startColor="@color/gold1"
                android:centerColor="@color/gold2" 
                android:endColor="@color/gold2"
                android:angle="@integer/vertical_shape" />
    </shape>

3- The line is a drawable too. you can find the files in the sdk and copy them into your project after modify them to change the color using gimp. You can combine this solution with setColorFilter() and android:tint and then you can select the background using hex triplets too. Read: further explanation

android-sdk-linux_x86/platforms/android-7/data/res/drawable

tab_bottom_left.xml,  
tab_bottom_right.xml,  
tab_indicator.xml  (define state changes)

android-sdk-linux_x86/platforms/android-7/data/res/drawable-mdpi

tab_focus.9.png (change color)
tab_focus_bar_left.9.png
tab_focus_bar_right.9.png
tab_press.9.png (change color)
tab_press_bar_left.9.png
tab_press_bar_right.9.png
tab_selected.9.png (change color)
tab_selected_bar_left.9.png tab_selected_bar_right.9.png
tab_unselected.9.png

Solution 3

What about the solution I proposed on this question?

You can customize the drawable of each button using the same used by native Android Tab bar(looking for resources in Android.jar to find the right drawables), plus you can customize additional behavious as you desire.

At the end, you will obtain something that is graphically similar to a tabbar, from an user perspective, but acts differently from a developer perspective.

Share:
19,310
Belgi
Author by

Belgi

My name is Amir Belgi. I welcome you to visit my blog: https://thetightlycoupled.wordpress.com

Updated on July 11, 2022

Comments

  • Belgi
    Belgi almost 2 years

    I opend a post about this before but I feel that I can now (after reading some other posts) better explain what I want and rephrase it so it will be better understand.

    I followed the tutorial about Tab Layout on the dev guide and I managed to create tabs with it, but I want to do some customization to it (and I did look on other posts, but either the code had many mistakes to it or it didn't answer what I'm looking for).

    1. The first problem I have is that the test is in most part over the icon instead of below it (I used an icon with dimensions 48x48 as recommended on the dev guide). I want the tab with to act like wrap_content does. I also want to change the text size (I think it's called the label).

    2. I want to use hex triplets to change the background color of the tabs, to change it between to situations : when this tab is the one selected and when it's not.

    3. I want to be able to change the color of the line that is below the tabs, I could not find any information on how to do this.

    The code I'm currently using to create a new tab is (from the dev guide):

        intent = new Intent().setClass(this, GroupsActivity.class);
        spec = tabHost.newTabSpec("groups").setIndicator("groups",
                          res.getDrawable(R.drawable.ic_tab_groups))
                      .setContent(intent);
        tabHost.addTab(spec);
    

    (groups is the tab name).

    Help is very much appreciated!

  • Belgi
    Belgi over 12 years
    looks very interesting, but the layout code gives me errors: " <LinearLayout android:id="@+id/tabbar" " gives me "The markup in the document following the root element must be well-formed." and at the end I get "error: Error parsing XML: unbound prefix" can you please check that code ? thanks for your help
  • SBerg413
    SBerg413 over 12 years
    Sorry - had some typos in there from while i was trying to take out the non-relevant parts. Should be good now. Keep in mind that i'm making references to specific drawables. You'll need to create your own and adjust the names accordingly.
  • Belgi
    Belgi over 12 years
    I'm still getting "The markup in the document following the root element must be well-formed." but this time it's on " <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="bottom"> "
  • SBerg413
    SBerg413 over 12 years
    Really sorry about that. This compiled for me.
  • SBerg413
    SBerg413 over 12 years
    Also, keep in mind that you're going to have to create your drawable images or you'll get a resource not found error. But, that's the whole point of this approach - to create custom drawables to replace the original tabs.
  • Belgi
    Belgi over 12 years
    @drawable/btn_first_on is a pic (png etc') ?
  • SBerg413
    SBerg413 over 12 years
    yes ... those will be your tab buttons.. make 2 for each tab.. on and off ... btn_first_on.png & btn_first_off.png
  • Belgi
    Belgi over 12 years
    ok, I used the code and it works, but I have two things I want to do and don't know how: A. I want to have some space between the icons, I tried adding padding but it didn't work. B.add text below the icons that is still a part of the button
  • SBerg413
    SBerg413 over 12 years
    A) it's not padding but margins that you should be trying to set. B) Make the text part of the image. You could also try adding the text to the button and positioning it, but that's not how I do it.
  • Aamir Shah
    Aamir Shah over 11 years
    Best answer.... my question : Why only 2 upvotes... deserves at least 100 upvotes.... :)
  • pepela
    pepela almost 11 years
    @SBerg413 I used this method also added HorizontalScrollView for LinearLayout and now I can't make that the selected button was in the middle, have you any suggestions? thanks