+5  A: 

I see a lot of StackOverflow questions/answers saying that due to various limitations in the way the TabHost is setup, it's best NOT to use activities as the content of tabs.

As self-appointed President of the Anti-Activity-Tab Alliance (AATA), that's certainly my position.

When an icon corresponding to a Person, Place, or Event is clicked, it fires off a VIEW Intent on a URI corresponding to that object; this is picked up by an Activity that then shows the object.

Note that this has nothing to do with having activities as the contents of tabs.

We can launch a new activity to show the map again, but now we have the map activity as the content of the tab, plus the show activity, plus the new map activity in the activity stack; given how resource intensive the map activity is, I'm guessing this is not the ideal way to go.

I'd avoid it if possible.

I'm worried that if we switch to the View based way of doing things, we'll have to do a LOT of housekeeping to intercept all the back events, try to switch out the views, etc., etc., as well as strongly coupling our program in a way we don't want.

This doesn't follow at all from what you wrote previously. Your "back events" will not change one iota between using Views as the contents of tabs and using Activities as the contents of tabs. Furthermore, this has nothing whatsoever to do with the "loose coupling" pattern you describe -- clicking on an icon in a list in a view in a tab is no different than clicking on an icon in a list in a view in an activity in a tab.

Just have your Show activity tell your, um, main activity to show a particular location, then the Show activity can finish(). The simplest way to do that without introducing a hard JVM coupling between the activities is to broadcast an Intent and register a BroadcastReceiver in the main activity. Upon receipt of this Intent, the main activity would update the map and set it to be the current tab. Of course, this approach is simpler if you have the main activity use Views for its tab contents.

Now, if you try to overhaul your application, such that navigating in a tab doesn't launch another activity, but rather keeps things within its own tab...that is a whole 'nuther kettle of fish.

CommonsWare
OK, I see your point that if we keep all the Intents set up the same way, and the Show activity is launched, then the back events are handled exactly the same. So I guess the real complexity (and the back event pain) would be if we programmatically switched out the contents of tabs rather than launched intents. E.g. the People tab which normally displays a List of people is told to switch to a Show person view when a given person is clicked on. In that case we'd have to deal with the stack of views ourselves?
I82Much
Also, how can one have a map inside of a tab without having an activity as the content of a tab? Is that possible?
I82Much
w/r/t your first comment, yes, you would need to do your own stack management. w/r/t your second comment, yes you can AFAIK. Make your main activity be a MapActivity rather than a TabActivity. The only thing TabActivity does for you is call setup() automatically on your TabHost, which you will then need to do yourself before adding any TabSpecs. Then, the MapActivity gives you what you need for putting the map in a tab. It's possible there are problems here, but off the cuff I think it should work OK.
CommonsWare
+4  A: 

It's a good practice to have one activity and multiple views for your tabs. However, that does mean you have to be careful to handle which tab is selected, different menus and context menus for each tab view, etc.

I guess my question is, is there a GOOD tutorial somewhere showing exactly how to do complex tasks with a TabHost? I've seen HelloTabWidget; I'm looking for something much more sophisticated than this.

I wrote a slightly better tutorial on my blog that demonstrates an interacting ListView and MapView as tabs. Here's the link: Android Tabs with interacting map and list views

The basics is to have a layout similar to the one in the HelloTabWidget tutorial, make your activity extend from MapActivity, extract the tabhost from the XML and make sure you call setup() on the tabhost. After that, adding views as the content of tabs, tab listeners, etc. are the same.

Here's a brief starting point for the class:

public class TabbedListMapActivity extends MapActivity {

private ListView listView;
private MapView mapView;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    tabHost = (TabHost) findViewById(android.R.id.tabhost);

    // setup must be called if not a TabActivity
    tabHost.setup();

    // setup list view
    listView = (ListView) findViewById(R.id.list);

    // setup map view
    mapView = (MapView) findViewById(R.id.mapview);

    // add views to tab host
    tabHost.addTab(tabHost.newTabSpec("List").setIndicator("List").setContent(new TabContentFactory() {
        public View createTabContent(String arg0) {
            return listView;
        }
    }));
    tabHost.addTab(tabHost.newTabSpec("Map").setIndicator("Map").setContent(new TabContentFactory() {
        public View createTabContent(String arg0) {
            return mapView;
        }
    }));
}

...

Josh Clemm
Now, this is a great answer! Thanks Josh, you saved my day.
Fabio Milheiro