A cross platform navigation group

Blog Feature

There are many differences between iOS and Android, but perhaps one of the most noticeable is iOS’ Navigation Group. This control is a fundamental part of iOS and a very useful and easy to implement navigation paradigm. It conforms to iOS’ design guidelines and provides you a “free” way of going back to your previous screen.

However, when you are developing a cross-platform app, you soon realize that there’s no equivalent on Android, or I should say, there wasn’t one until Android 3.0. If you’re not an Android user, you probably haven’t noticed, mainly because when you create an Android App on Titanium it defaults to API Level 8 (Android 2.2 Froyo), the second most used Android version in the world. If you run a default Android App it will look something like this.

The window on this app is basic, simple, with a thin titlebar with no room to add your navigation button. The workaround I often see is to use views to simulate iOS’ titlebar and back button, and although this solves the problem, leads to an Android app that looks like an iOS app.

In mid 2011 Android 3.0 was released and with it, a brand new UI named Holo, which provides a more mature and standardized way of approaching Android user interfaces. One of the components of Holo is the ActionBar, and this component allows us to provide the same navigation paradigm as the Navigation Controller.

To use the ActionBar on your app, you need to tell Titanium to use and Android API that has these features. The one I use is API Level 14, which corresponds to Android 4.0. To change this, go to your tiapp.xml, scroll to the Android section and inside the android tag add:

14

This tells Titanium that to use the API from Android 4.0 (Ice Cream Sandwich). Then, to use the ActionBar add this also to the Android section:

   

This sets an Android pre-defined theme. The Theme could be any of the following:

@android:style/Theme.Holo
@android:style/Theme.Holo.Light
@android:style/Theme.Holo.Light.DarkActionBar

Now if you run the same simple app, you’ll see that it now looks like any other modern Android app.

I’ll explain more about the ActionBar in a future blog post.

Using Alloy Magic

This can also be accomplished using Titanium Classic syntax, but with Alloy is much easier. I have created a simple template app for you to use, experiment with and expand, but the real heavy lifting is performed by a small function I call openWin. This function is essentially a cross-platform window opener, which receives a pointer to the iOS Navigation Group and the name of the view file to open. If running on iOS, it opens the view as a new child of the Navigation Group. If running on Android, it simply pays no attention to the Navigation Group and creates a new activity with an up button and an event handler to close the activity.

exports.openWin=function(navGroup,winName){
    var w=Alloy.createController(winName).getView();

    if (OS_ANDROID){
        w.addEventListener('open',function(e){
            if (! w.getActivity()) {
                Ti.API.error("Can't access action bar on a lightweight window.");
            } else {
                actionBar = w.activity.actionBar;
                if (actionBar) {
                    actionBar.displayHomeAsUp=true;
                    actionBar.onHomeIconItemSelected = function() {
                        w.close();
                    };
                }
                w.activity.invalidateOptionsMenu();
            }
        })
        w.open();
    }else{
        navGroup.open(w,{animated:true});
    }
}

Here’s a video of how it looks and behaves, running on Android 4.1, iOS6 and iOS7, all from the same source code.

Notes: By using API Level 14, you’re actually limiting your target users to only those with an Android 4.x device. You could use API Level 11 and target all devices since 3.0, but in this case you’ll have no access the Holo.Light.DarkActionBar theme. More about API Levels at https://developer.android.com/guide/topics/manifest/uses-sdk-element.html.

Devices with Android 2.x have no access to the ActionBar. For these devices I suggest you use the ActionBarClone Alloy Widget that will bring an emulated ActionBar on any Android version.

28 COMMENTS

  1. Hello,

    I have an issue on android.
    It works well with SKD 3.1.1GA but it doesnt with 3.1.2GA.
    Sometimes two ActionBar are displayed verticaly.

    Pascal.

  2. I’m trying to use the classic syntax of titanium to use navigation on android but I don’t know how to do that…
    can you give a sample of this cross-platform navigation group?

    and with the change of navigation on new version of Titanium 3.1.3 will have some issue with your code?

    Thanks

      • Your “Titanium Classic version at..”-link is not working and referring to the same Alloy code ? In your Gist response to DHennrich I’m seeing first line that requires /lib/xpngclassic, but I can’t find xpngclassic.js in the code.
        For newbees and none-Alloy users (like me) that is confusing. Would you please cough up the Classic version as well ? Or tell a bit more how to use this for none-Alloy? Thanks.

  3. Thanks Ricardo!

    It’s very very very useful

    Thank you very Much

    By the way… through your video I can see that on the new version of Titanium it will work too.

  4. Does this really work with Titanium 3.1.3? I just tried it out for android (cloned your git repo) and after clicking on the first button ‘Open blue window’ I’m getting this exception:

    [ERROR][TiApplication( 1517)] (main) [317,5971] Sending event: exception on thread: main msg:java.lang.NullPointerException; Titanium 3.1.3,2013/09/18 12:01,222f4d1
    [ERROR][TiApplication( 1517)] java.lang.NullPointerException
    [ERROR][TiApplication( 1517)] at org.appcelerator.titanium.proxy.ActionBarProxy.handlesetDisplayHomeAsUp(ActionBarProxy.java:178)
    [ERROR][TiApplication( 1517)] at org.appcelerator.titanium.proxy.ActionBarProxy.handleMessage(ActionBarProxy.java:201)
    [ERROR][TiApplication( 1517)] at android.os.Handler.dispatchMessage(Handler.java:95)
    [ERROR][TiApplication( 1517)] at android.os.Looper.loop(Looper.java:137)
    [ERROR][TiApplication( 1517)] at android.app.ActivityThread.main(ActivityThread.java:4745)
    [ERROR][TiApplication( 1517)] at java.lang.reflect.Method.invokeNative(Native Method)
    [ERROR][TiApplication( 1517)] at java.lang.reflect.Method.invoke(Method.java:511)
    [ERROR][TiApplication( 1517)] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    [ERROR][TiApplication( 1517)] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    [ERROR][TiApplication( 1517)] at dalvik.system.NativeStart.main(Native Method)
    [ERROR][AndroidRuntime( 1517)] FATAL EXCEPTION: main
    [ERROR][AndroidRuntime( 1517)] java.lang.NullPointerException
    [ERROR][AndroidRuntime( 1517)] at org.appcelerator.titanium.proxy.ActionBarProxy.handlesetDisplayHomeAsUp(ActionBarProxy.java:178)
    [ERROR][AndroidRuntime( 1517)] at org.appcelerator.titanium.proxy.ActionBarProxy.handleMessage(ActionBarProxy.java:201)
    [ERROR][AndroidRuntime( 1517)] at android.os.Handler.dispatchMessage(Handler.java:95)
    [ERROR][AndroidRuntime( 1517)] at android.os.Looper.loop(Looper.java:137)
    [ERROR][AndroidRuntime( 1517)] at android.app.ActivityThread.main(ActivityThread.java:4745)
    [ERROR][AndroidRuntime( 1517)] at java.lang.reflect.Method.invokeNative(Native Method)
    [ERROR][AndroidRuntime( 1517)] at java.lang.reflect.Method.invoke(Method.java:511)
    [ERROR][AndroidRuntime( 1517)] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
    [ERROR][AndroidRuntime( 1517)] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
    [ERROR][AndroidRuntime( 1517)] at dalvik.system.NativeStart.main(Native Method)

    I tried it out for Android 4.0.3 and 4.3..

    Any advices here?

  5. Hi Emre,

    I just re-downloaded the repo and ran it on a Galaxy S4 with 4.2.2 stock and it does work. Did you try on device or emulator?

    R

  6. hi Ricardo,

    I did it inside my emulator, have I missed a warning for not using it inside emulators somewhere?

    cheers and thanks
    emre

  7. Demo app works fine in iOS 7.x simulator. Android emulator crashes when launching with this error:

    [ERROR] : Emulator process exited with code 1
    [ERROR] : Build process exited with code 1
    [ERROR] : Project failed to build after 495ms
    [ERROR] Application Installer abnormal process termination. Process exit value was 1

    Using android 4.4, WXGA800 screen. I don’t know how to fix this error, so cannot get code to run.

      • Hi Ricardo,
        Here are my stats:
        – app: android mobile
        – android sdk: 4.4
        – Ti SDK 3.1.2
        – os: osx mavericks
        – studio Titanium Studio, build: 3.1.3.201309132423
        – output: Android emulator WXGA800 7in tablet

        • Are you able to run other Android projects? This appears to be related to Android configuration. With 3.1.x you also need to have installed Android SDK 3.2.2, so make sure you also have it installed.

          • Ricardo,
            Unless I missed something, there is no android 3.2.2. Did you mean 2.3.3 (API10)? I have that installed. And yes, I am able to launch other android apps with the emulator.

  8. Hi, am new to android ,currently am learning titanium with classic
    i need how to implement the desight
    how to run in emulator?
    can you help me friends..

Comments are closed.