Titanium Mobile – Dynamic TabGroups on android

A question that is frequently posed on the Titanium Q&A forum is how to manipulate TabGroups on android. On iOS, Titanium allows you to add new tabs to your TabGroup. You can even have multiple TabGroups (so for example, your app could branch off into multiple windows, each with its own TabGroup.

But on android, you can only have one TabGroup active at a time, and you can’t alter the tabs in the group once it’s created. In theory, you can create a new one and close the previous one to effectively change the tabs in a TabGroup.

But executing this is easier said than done.

In my first attempt, I thought that I could just create a new TabGroup, open it, and then close the existing TabGroup. This worked nicely on iOS, but on android, it had the unfortunate side effect of closing the app. Not exactly what we were hoping for.

So I thought that maybe I could prevent the app from closing by creating a window first and opening the TabGroup after that window is open. I was getting closer, but I couldn’t tell, because the app closed as soon as I closed the TabGroup.

OK — time to pay more attention to the docs. I need a heavyweight window to keep the app open.

A window is heavyweight if any of these conditions are met:

  • any of these properties are set to either true or false:
    • fullscreen
    • navBarHidden
    • modal
  • the window’s windowSoftInputMode property is set to any of the Ti.UI.Android constants
  • the tabOpen property is true (WTF is this property? Can’t find it in the docs!)
Note that I think the API docs are wrong, as they state: The createWindow call creates a heavyweight window if any of the following properties are true on creation: fullscreen, navBarHidden, modal, windowSoftInputMode; you can actually set fullscreen, navBarHiden, or modal to false and still get a heavyweight window.

So I create a main window for the app. To keep it from being noticeable to the end user, I set its background black and set it to fullScreen with no nav bar:

Once I created a heavyweight window (by setting navBarHidden to false), the app stayed open while I was changing out the TabGroup. So that was good. Now the bad news was that when I hit the hardware “back” button, the TabGroup closed, but the app didn’t exit; it of course, took us back to the heavyweight window that we created. We really didn’t want to show that window to the user.

Feeling pretty pleased with myself about how this was working, I continued some testing, and unfortunately, I found that the app was unstable. It would often crash while changing out the TabGroup. It seems that the times it did work were an exception, definitely not the rule. If I was interpreting the debug messages correctly, closing the TabGroup caused Titanium to close the main activity, which made the app shut down. The subsequent calls to create a new TabGroup were throwing exceptions, probably because there was no current activity.

Thankfully, I stumbled upon a workaround. I found that if I put a button on the main window and opened the TabGroup from a click on that button, I could hit “back” to close the TabGroup, and I could use the button to open a new TabGroup. That was stable. I even found that if I opened the TabGroup from the button, I could programmatically replace the TabGroup, and all worked well.

So it seems that you can’t just open the main window and immediately open the TabGroup. There’s some sort of internal initialization that has to happen before you can open the TabGroup if you want the main window to serve as a true heavyweight “anchor” for the app.

My fix was to put the call to create and open the TabGroup inside an “open” event listener for the main window. This seems to give the main window the initialization it needs before creating the TabGroup.

Now I had an app that opened a heavyweight window and automatically opened a TabGroup. I was able to programmatically replace the TabGroup and keep the app stable. The problem I had was that if the user hit the hardware “back” button, he would be left at the main window. For this app, the main window is a sort of “dummy” window, just a placeholder to keep the app running while the TabGroup is being rebuilt. We don’t want the user to be aware of this window.

So I had to add some logic to make sure the app closes when the user hits the “back” button. Basically, when we get a “close” event that is not triggered by our own replacement code, we need to close the main app window, which has the exitOnClose property set to true.

The result is that we have a tabbed UI for iOS and android that allows us to change out the tabs based on user behavior.

app.js

ApplicationTabGroup.js

Back to Titanium Mobile: Beyond the prototype

2 thoughts on “Titanium Mobile – Dynamic TabGroups on android

  1. Brilliant. This saved my life!

    I did it in top-level heavyweight window’s focus event though:

    Alloy.Globals.tabGroupClosing = false;
    Alloy.Globals.topWindowStarted = false;

    Alloy.Globals.topWindow.addEventListener(‘focus’, function(e) {
    if (Alloy.Globals.topWindowStarted) {
    if (!Alloy.Globals.tabGroupClosing) {
    Alloy.Globals.topWindow.close();
    }
    else {
    Alloy.Globals.tabGroupClosing = false;
    }
    }
    else {
    Alloy.Globals.topWindowStarted = true;
    }
    });

Leave a Reply

Your email address will not be published. Required fields are marked *