Search code examples
androidbackgroundscaleandroid-tabhosttabwidget

TabHost / TabWidget - Scale Background Image?


I need to scale my TabWidget background images so they maintain aspect ratio.
I am using a TabHost with a TabWidget. I am then using setBackgroundDrawable to set the images.

I found a close answer here - Background in tab widget ignore scaling. However, I'm not sure just where to add the new Drawable code. (Working with the HelloTabWidget example, none of my modules use RelativeLayout, and I don't see any layout for "tabcontent".)

I also found this thread - Android: Scale a Drawable or background image?. According to it, it sounds like I would have to pre-scale my images, which defeats the whole purpose of making them scaleable.

I also found another thread where someone subclassed the Drawable class so it would either not scale, or it would scale properly. I can't find it now, but that seems like a LOT to go through when you should just be able to do something simple like mTab.setScaleType(centerInside).

Here's my code:

main.xml:

<?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"
    android:background="@drawable/main_background"> 
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    <FrameLayout
            android:id="@android:id/tabcontent"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_weight="1"/>        
    <TabWidget
            android:id="@android:id/tabs"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" 
            android:layout_weight="0"/>
    </LinearLayout>
</TabHost>

main activity:

        tabHost.setOnTabChangedListener(new OnTabChangeListener() { 
            TabHost changedTabHost = getTabHost();
            TabWidget changedTabWidget = getTabWidget();
            View changedView = changedTabHost.getTabWidget().getChildAt(0);

            public void onTabChanged(String tabId) { 
                int selectedTab = changedTabHost.getCurrentTab();
                TabWidget tw = getTabWidget();

                if(selectedTab == 0) {
                    //setTitle("Missions Timeline");
                    View tempView = tabHost.getTabWidget().getChildAt(0);
                    tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_timeline_on));
                    tempView = tabHost.getTabWidget().getChildAt(1);
                    tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_map_off));
                    tempView = tabHost.getTabWidget().getChildAt(2);
                    tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_search_off));
                    tempView = tabHost.getTabWidget().getChildAt(3);
                    tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_news_off));
                    tempView = tabHost.getTabWidget().getChildAt(4);
                    tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_license_off));
                            //ImageView iv = (ImageView)tabHost.getTabWidget().getChildAt(0).findViewById(android.R.id.icon); 
                            //iv.setImageDrawable(getResources().getDrawable(R.drawable.tab_timeline_on)); 
                            //iv = (ImageView)tabHost.getTabWidget().getChildAt(1).findViewById(android.R.id.icon); 
                            //iv.setImageDrawable(getResources().getDrawable(R.drawable.tab_map_off)); 
                    } else if (selectedTab == 1) {
                        //setTitle("Spinoffs Around You");
                        View tempView = tabHost.getTabWidget().getChildAt(0);
                        tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_timeline_off));
                        tempView = tabHost.getTabWidget().getChildAt(1);
                        tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_map_on));
                        tempView = tabHost.getTabWidget().getChildAt(2);
                        tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_search_off));
                        tempView = tabHost.getTabWidget().getChildAt(3);
                        tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_news_off));
                        tempView = tabHost.getTabWidget().getChildAt(4);
                        tempView.setBackgroundDrawable(getResources().getDrawable(R.drawable.tab_license_off));
}

I also tried 9patch images, but they wind up being too small.

So, what's the best way to go about this?


Solution

  • I suspect the reason you're struggling to find an out-of-the-box way to perform background image scaling is that it's not generally seen as good practice. Scaling of images tends to introduce artifacts and banding which can make your UI look nasty. The exception to this is within an Imageview, but I would argue the scaling support this class provides is more intended to support things like photos or web content (i.e. images you have not shipped with your app).

    Your UI layout should be designed in such a way that any resource-based background images should be pre-scaled to the correct resolution for the density / screen size of the device. In other words you should be providing multiple versions of each drawable to cater for multiple screen densities and sizes using the resource folder naming convention outlined in the Supporting Multiple Screens dev guide.

    The UI should then be laid out such that any slight differences of screen size between devices can be handled without needing to scale background images of its contained views. Obviously I don't know what your proposed UI looks like so it's difficult to make any concrete suggestions, but generally I use simple background block colors or ShapeDrawables to dynamically fill the space between views.

    However, if you're really really sure you want to scale your background images despite the preaching above :-), why not try using a ScaleDrawable to wrap your existing drawable?

    If you know the height and width of both your view and background image drawable, then you can do something like:

    Drawable backgroundDrawable = getResources().getDrawable(R.drawable.tab_license_off); 
    tempView.setBackgroundDrawable(
        new ScaleDrawable(
            backgroundDrawable,
            Gravity.CENTER,
            tempView.getWidth() / backgroundDrawable.getIntrinsicWidth(),
            tempView.getHeight() / backgroundDrawable.getIntrinsicHeight());