Can I use default tab styling in my custom tab view?
Asked Answered
N

5

11

My main activity is a TabActivity, but my tabs don't require icons. I know I can omit the icon by using the overloaded TabHost.TabSpec.setIndicator(CharSequence label) method, as answered in this question.

But the space where the icon would go is still reserved, so a lot of space is wasted. This has already been remarked in this question. It seems I can't just reduce the tab height. I'll have to supply my own View with the TabHost.TabSpec.setIndicator(View view) overload.

Fine, but I want to make sure the rest of the styling (background/coloring) remains consistently 'android' across all devices, just as if I'd used a default tab indicator. So I don't want to supply my own colors.

I've found @android:color/tab_indicator_text and using it for the TextViews text-color seems to work as expected. But I don't know where to get the default tab-background-colors. I've also discovered the @android:style/Widget.TabWidget style, but applying it to neither the TextView nor the surrounding LinearLayout seems to help.

This is my tab indicator view xml file, taken from some tutorial:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tabLayout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center"
    android:padding="12dip" >

    <TextView
        android:id="@+id/tabText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/tab_indicator_text" />
</LinearLayout>

How do I get 'android default' tab styling on this?


Edit: Next, I tried to add a default tab through setIndicator(CharSequence), take the getBackground drawable of its indicator view, clear all tabs, add my custom views (see above), then put the drawable I retrieved in setBackground for the new indicators.

The result looks sort of right and sort of wrong. For one, they're too high again, so I gained nothing. For another, the background states of all tabs are now linked somehow (active, highlighted, selected, etc.). Using mutate() on the drawable(s) didn't help at all. Next trick: I added three default tabs, took all of their backgrounds, cleared them, and gave my own three tabs one background each.

Well, that solves the second problem. Their states are no longer linked. But they're still too high. So I reduced the height of the TabWidget using updateViewLayout. This works, but leaves a discolored bar at the top of the tab indicators.

Actually, I just end up with the same situation as when using the TabHost.TabSpec.setIndicator(CharSequence label) method. And anyway, all these hacks make me feel dirty. Isn't there an elegant way to get default android styling on a custom view?

Narcisanarcissism answered 25/11, 2011 at 15:45 Comment(3)
Could you post an image of the exact problem? I'm really inclined to say it might be because of the padding being 12dp on all sides, even though that might be a really stupid answer. I've used pretty much the same method as you have described several times but I've never had any problem like this. Then again, I didn't use padding.Unmuzzle
Which same method have you used several times? And which problem do you not have, exactly? :-) I've described several possible approaches and several problems.Narcisanarcissism
Hmm. After reading back my comment and your original question, I have to admit I really don't know what I was referring to. Must have been one of my sleep-deprived midnight postings. Please ignore it, it's of no help to you ;) Hope you find a fix to your problem though!Unmuzzle
C
10

This code taken from android sources tab_indicator.xml

<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="@android: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>

You can try to just take this code, delete the ImageView, change layout_centerHorizontal in the TextView to layout_center, reduce height of the RelativeLayout, and provide this view in TabHost.TabSpec.setIndicator(View view). There are references only to @android resources, so you do not need create any drawables or text styles.

EDIT

The standard android drawable background for the tab indicator is not visible from an app, but it is possible to do it like this:

TabSpec tc = getTabHost().newTabSpec("tag");
tc.setIndicator("indicator1");
tc.setContent(content);
getTabHost().addTab(tc);
getTabHost().findViewById(android.R.id.icon).setVisibility(View.GONE);
TextView tv = (TextView) getTabHost().findViewById(android.R.id.title);
RelativeLayout.LayoutParams rllp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
rllp.addRule(RelativeLayout.CENTER_IN_PARENT);
tv.setLayoutParams(rllp);

Then it is possible to reduce the height of the tabs:

RelativeLayout parent = ((RelativeLayout) tv.getParent());
ViewGroup.LayoutParams vglp = parent.getLayoutParams();
vglp.height = android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
parent.setLayoutParams(vglp);

The result will be: this

Capablanca answered 12/12, 2011 at 6:25 Comment(9)
I'm afraid that @android:drawable/tab_indicator is not available for me. Eclipse doesn't autocomplete it and won't compile with it. I think it's private to android itself. Not part of the public API, which gives me the suspicion that there actually is no elegant solution and I'll be forced to use some hack, or give up on the idea entirely.Narcisanarcissism
Yep, you are right. There can be other hack - use setIndicator(CharSequence), then add TabSpec to TabHost, find view in TabHost, setVisibility(View.GONE) for ImageView (findViewById(android.R.id.icon) - this id is visible), and change gravity for TextView, but it seems too complicated.Capablanca
I think it would also result in a discolored bar at the top of my tab, just like my other attempts to reuse the background drawable of the standard tab indicator, and then resize it. Anyway, I'm not in a position to try it out right now. What do you say, if you try it out and report the results before the bounty expires, you can have it. ;-)Narcisanarcissism
Edit original answer. This is possible, but quite dirty :(Capablanca
Now, the important bit: can you reduce the height of this tab indicator without any visual anomalies?Narcisanarcissism
It is possible, but it is hack, and may be there is a better wayCapablanca
let us continue this discussion in chatNarcisanarcissism
Reducing the height by setting it to WRAP_CONTENT doesn't seem to work for me (Android 2.3.6). Setting it to a constant height gives the the same problem as always: a discolored bar at the top of the tab indicators. I think I'll give this up and keep the tab at its original height. :-)Narcisanarcissism
This is incorrect answer ! In holo the indicator is not even a RelativeLayout anymore. Its is Linear.Linneman
H
2

I think the best option would be to customize the Tabs aspect using xml files called selectors (that you will put on your "drawable" folder), a custom tab_indicator.xml on your "layout" folder, a style for the text and a theme using such style.

This is the tab_indicator_selector.xml that goes in the "drawable" folder and is required by the tab_indicator.xml:

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

And this is the tab_pressed.xml state selector:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:top="53dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/white" />
        </shape>
    </item>
    <item android:top="53dp" android:bottom="1dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/white" />
        </shape>
    </item>
    <item android:left="2dp" android:right="2dp"> 
        <shape android:shape="rectangle">
            <gradient android:angle="90" 
                android:startColor="@color/black"
                android:endColor="@color/white" />
            <stroke android:width="2dp" android:color="@color/red" />
        </shape>
    </item>
</layer-list>

The other states can be achieved by creating tab_focused.xml, tab_selected.xml and tab_unseleceted.xml and setting the right android:state_XXX combinations.

The tab_indicator goes like this:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dip" 
    android:layout_height="52dip" <!-- Control the height of the Tab here! -->
    android:layout_weight="1"
    android:orientation="vertical"
    android:background="@drawable/tab_indicator_selector" 
    android:layout_marginTop="2dp"
    android:padding="2dp">

   <!-- Generally here goes an ImageView, but you said you don't want it. -->

    <TextView android:id="@+id/tab_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:textSize="11dp" 
        android:textStyle="bold" 
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        style="?android:attr/tabWidgetStyle"
    />    
</RelativeLayout>

On the implementation side, should look like this:

private void setTabs(){
    tabHost = getTabHost();
    addTab(R.string.tab_title_something, R.drawable.tab_something_selector, new Intent().setClass(this, PlacesNearbyTab.class));
}

private void addTab(int labelId, int drawableId, Intent intent) {
    TabHost.TabSpec spec = tabHost.newTabSpec("tab" + labelId);
    View tabIndicator = LayoutInflater.from(this).inflate(R.layout.tab_indicator, getTabWidget(), false);
    TextView title = (TextView) tabIndicator.findViewById(R.id.tab_title);
    title.setText(labelId);
    spec.setIndicator(tabIndicator);
    spec.setContent(intent);
    tabHost.addTab(spec);

}

I hope it helps. It is a long solution, but the use of themes and styles is pretty handy when the App has grown, or when you want to change its look on version 2.

Finally I suggest you to check out the new SDK version 14 and the Holo theme. The tabs on Holo are both for Tablets and Phones and you can make them with and without icons.

Honegger answered 8/12, 2011 at 0:50 Comment(3)
Well, I already do something like this. But I'm afraid that it does not address my problem, which is: How do I get default android tab-styling on my custom tab indicator, without supplying my own colors? You are using a @drawable/tab_indicator_selector there. But I don't know how to write it to achieve my goal.Narcisanarcissism
Precisely I wanted you to research a little bit more about the selectors... but here it goes. ( I will modify my answer to include it).Honegger
I know how to use selectors. But you're still supplying your own colors. :-) I pretty much already got as far as this, but what I'm having problems with is querying the system for the default colors a tab should have. I want my app to look native across all versions/skins of the OS. So I can't just make my tab white, black and red.Narcisanarcissism
O
0

I'm not sure if this will center the text, but you can change the height of the tab without changing the default Android background.

Try this:

TabHost tabs = //initialize your TabHost here
tabs.setup();
TabSpec tspec1 = tabs.newTabSpec("First Tab");
tspec1.setIndicator("Actions");
tabs.addTab(tspec1);


tabs.getTabWidget.getChildAt(0).getLayoutParams().height = (int) (35 * 1.5);

Where 35 is the height in pixels, and multiplying by 1.5 makes the height 35dp.

Good Luck.

Obstinate answered 7/12, 2011 at 16:22 Comment(1)
Thanks, but I already tried this, and it has the following problems: * It won't center the text vertically. * It leaves a discolored bar at the top of the tab.Narcisanarcissism
B
0

could you try the following sample code, which are taken from default tab_indicator.xml and make a slight modification accroding to the demand you describes. Thanks!

<LinearLayout
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:gravity="center"
android:background="@android:drawable/tab_indicator">

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

</LinearLayout>
Bozovich answered 12/12, 2011 at 7:20 Comment(1)
Jin35 just suggested the same, but I'm afraid it doesn't work. See my comment on his/her post.Narcisanarcissism
H
0

You can find the res color files for every platform under /path-to-android-sdk/platforms/ and you can use @android:style/ or @android:color/ to access the default resources from XMLs...

But as you mentioned, you are aware that colors and styles of TabWidgets and other Android UI Views change from version to version and between brands of devices.

Even if you can do what you want to do, your Tabs would look different between versions and brands of devices... what is not the usual.

I would implement my own Tab styles with one of the given solutions. Good luck tho!

Honegger answered 13/12, 2011 at 1:15 Comment(1)
Actually, that's the whole point. :-) I don't need my app to have its own 'style'. It's not that kind of app. I'd like it to look native on whatever device it's run.Narcisanarcissism

© 2022 - 2024 — McMap. All rights reserved.