2010-03-03

Behaviour of TabHost: Why tab content is empty???

The TabHost is designed to have different View or Activity for each Tab. But, it's not necessary and sometimes wastes memory.

My activity has a TabHost as the root of UI hiearchy and a ListView as its tab content.


The view hierarchy is as follows,

You can see that a TabHost actually must have a LinearLayout vertically compose a TabWidget with id android:tabs and a FrameLayout with id android:tabcontent.

Every Tab change will have previous Tab closed and next Tab opened. The close of a Tab is to set the associated View gone and the open of a tab is to set the associated view visible. The same logic applied when a TabSpec is created and attached to the TabHost.

If there is only one Vew associated with tabs and your UI is not carefully designed. You might end up with empty ListView on application start like what I have encountered.

public class TabHost extends ... {
    // ...
    public void addTab(TabSpec tabSpec) {
        if (tabSpec.mIndicatorStrategy == null){
            throw new IllegalArgumentException("you must specify a way to create the tab indicator."); 
        }
    
        if (tabSpec.mContentStrategy == null) {
            throw new IllegalArgumentException("you must specify a way to create the tab content");
        }
    
        View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
        tabIndicator.setOnKeyListener(mTabKeyListener);
        mTabWidget.addView(tabIndicator);
        mTabSpecs.add(tabSpec);
        if (mCurrentTab == -1) {
            setCurrentTab(0);    // Set first associated View visible.
        }
    }
    // ...
}                                              

public class TabSpec {
    // ...
    public TabSpec setContent(int viewId) {
        mContentStrategy = new ViewIdContentStrategy(viewId); // Set viewId GONE.
        return this;
    }

    // ...
}
From Android source code above, when a TabSpec is given a View, it sets this View GONE and only the first Tab has a chance to set its associated View to be VISIBLE. In fact, in my case, it's always the same ListView.

But, if you have code as follows,
// ...
    final TabHost tabHost = getTabHost();
    final int tabCount = mTabData.size(); 
    for (int position = 0; position < tabCount; position++) {
        // get label and icon...
        TabSpec tabSpec = tabHost.newTabSpec(String.valueOf(position));
        tabSpec.setInficator(label, icon).setContent(android.R.id.list);
        tabHost.addTab(tabSpec);
    }
    // ...
Every setContent() of a TabSpec sets associated android.R.id.list GONE. This is why I can't see ListView when application starts. To fix this, you can just save created TabSpec in an ArrayList inside loop and added into later.