Sam Croft

A designer/developer on HTML, CSS, JavaScript and PhoneGap

Building an Android PhoneGap app ‘reveal’ menu/tabbar that doesn’t rely on the use of position: fixed

  • 11 comments

Filed in: android, jQuery, PhoneGap

Last month I started developing for Android for the first time, using PhoneGap and Eclipse. Initially this was a bit of shock, I was rather outside of my comfort zone having used Xcode and iOS for the last three months.

After packing away my lovely iPhone 4 test phone and pulling out the ol’ HTC Desire I went through the laborious process of setting up an Android development environment—slightly more confusing than installing Xcode, but once set up there is none of the certificate bullshit to slow you down. Yippee.

iPhone developers get all the community love

Alright, bit of an overreaction but from my initial research (Googling, Stackoverflowing and Grouping) it was abundantly clear that the vast majority of mobile webkit questions were by iOS developers in a pickle.

Maybe Android developers are more adept and just don’t need to ask or write about their pickles! Or could it be that Android and it’s YoYo what’sit firmware is vastly superior to iOS and there are no bugs to be found? Yay.

No?

It seems, just like us at RITH, people tend to develop for iOS and figure out (if at all) Android afterwards. No doubt that’s a trend that may eventually change when you consider the consistent rise in Android sales vs iPhone. And it will be a welcome trend, as developing just for the iPhone is dangerous path. One that you can see people wandering down all the time. But that’s another post entirely.

Anyway, on to the actual issue I intended to write about.

The problems with no position: fixed support on Android phones using firmware 2.1 and lower

The previous PhoneGap app that we made at RITH was developed for the iPhone and we made use of the wonderful NativeContols plugin, allowing us to create native iOS toolbars and tabbars. As of writing there are no native UI control plugins for Android PhoneGap (although it is penned in their roadmap) apps so our first consideration was how to integrate a menu system using webkit controls.

Although iOS doesn’t support position: fixed, I was hopeful that Android would. And it turns out they do! But only on firmware 2.2 and up. This was a bit of a bummer as using position: fixed one could easily integrate a sticky tabbar to the top or bottom of the app. But only supporting the latest firmware was not something to consider.

The solution: using the Android menu button to display a menu interface

PhoneGap for Android makes use of a few really cool functions that allow you to capture physical button presses on the phone itself for the menu, back and search buttons. But for the purpose of this example, I’m just interested in the menu button event, line 2577 of phonegap.js (0.9.3).

KeyEvent.prototype.menuTrigger = function() {
    var e = document.createEvent('Events');
    e.initEvent('menuKeyDown');
    document.dispatchEvent(e);
};

What these lines do is create a new document event called menuKeyDown that we can watch for in our app, like so:

document.addEventListener('menuKeyDown', function(){
    alert('You pressed the menu button');
});

Now we have an event listener attached the menu button we can create expected native functionality by displaying an app menu when the menu button is pressed.

The HTML foundation

Right, let’s consider a really simple framework we might build using PhoneGap. Nothing complicated here. A menu and some content boundaries. The aim will be to hide and show the menu on demand.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>PhoneGap Android menu - 2.1 friendly</title>

	<script src="phonegap.js"></script>
	<script src="jquery.min.js"></script>

	<link href="style.css" rel="stylesheet" />
</head>
<body>
	<nav>
		<ul>
			<li><a href="#">Button 1</a></li>
			<li><a href="#">Button 2</a></li>
			<li><a href="#">Button 3</a></li>
			<li><a href="#">Button 4</a></li>
		</ul>
	</nav>
	<div id="content">
		<p>Your app page content.</p>
	</div>
</body>
</html>

The CSS groundwork

And let’s add some really basic CSS to flesh out the menu.

nav {
	position: absolute;
	width: 320px;
	top: 0;
	background: #181818;
}
	nav ul {
		margin: 0 0 0 4px;
		padding: 0;
		height: 58px;
		list-style-type: none;
	}
		nav ul li {
			margin: 0;
			padding: 0;
			width: 75px;
			display: inline-block;
			text-align: center;
		}
			nav ul li a {
				display: block;
				line-height: 58px;
				text-decoration: none;
				color: #333;
				background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F8F8F8), to(#D8D8D8));
				-webkit-border-radius: 3px 3px 0 0;
			}

At this point the menu sits neatly at the top of the app window, visible at all times, and will overlap any content beneath it.

From this starting point I used a sprinkle of jQuery to toggle the visibility of the menu binding the function to the menuKeyDown event. While this worked fine, as soon as you consider scrolling you’re faced with viewport problems, in not being able to see the menu if you scroll past the height of the menu.

Using jQuery to calculate new ‘top’ values for position: absolute elements

It was clear from here that the solution to ‘fixing’ the menu in place would require understanding the height of the current section of the app that was being viewed. Fortunately jQuery makes this a breeze and we can use scrollTop() to calculate where abouts the top of the viewport area is relative to the actual size of the document. I.e. how many pixels are above the current visible area of the document.

Before implementing this function I needed to make a change to the menu element in my CSS.

nav {
	position: absolute;
	width: 320px;
	top: -58px;
	background: #181818;
}

Setting the top value of the menu element to -58px (the height of the menu) means that the menu will not be visible, until our function instructs it to be displayed.

document.addEventListener('menuKeyDown', function(){
	var newTop = $(window).scrollTop();
	$('nav').css('top', newTop + 'px');
});

These few lines bind a function to the menuKeyDown event, executing when the Android menu button is pressed. The first thing to calculate is the height above the currently viewed area using jQuery’s scrollTop method. Once we have this value we can alter the CSS for the menu element (here it’s just a nav element) and give it a new top value.

The end result is that when the function fires the menu will appear flush to the top of the app window, just like you would expect of a native Android app.

Using webkit-transitions to add a bit more polish to the Android menu

While this is all rather splendid, what would make it nicer would be to have the menu slide into view, rather than just popping into position. This is simple to accomplish by just adding a single line of CSS to the menu nav element.

nav {
	position: absolute;
	width: 320px;
	background: #181818;
	top: -58px;
	-webkit-transition: top ease 0.4s;
}

Using the -webkit-transition property will can specify changes in the top value to ease into view with an animation duration of 0.4s. This ensures that if the top value changes from -58px the change will be reflected by an animation rather than a sudden snap to its new position. Lovely.

Ah yes, and finally we should change our Javascript function to determine whether the menu is currently in view or not, in order to hide or show it respectively.

document.addEventListener('menuKeyDown', function(){
	var nav = $('nav');
	
	if(!nav.hasClass('active')) {
		var newTop = $(window).scrollTop();
		nav.css('top', newTop + 'px');
		nav.addClass('active');
	} else {
		nav.removeClass('active');
		nav.css('top', '-58px');
	}
	
	return false;
});

Here I have effectively integrated my own toggle function by adding or removing the class ‘active’ and using that to determine whether to set the menu as visible or not.

Note: for repeated use of same jQuery object I’m using a variable for an ounce of performance.

Room for improvement, of course

This is by no means a finished component to drop into a PhoneGap Android app. It does the job, but needs to be refined somewhat. There are two main areas that require attention:

  1. If the app content is long and you have scrolled to the bottom and pressed the menu button, the speed of appearance will be very sharp rather than the intended ease into view. It could be more effective to position the top value, off screen (i.e. to the left or right of the window), and then alter the right/left value to ease into view. This will always yield the same speed of appearance but obviously comes from the side rather than the top.
  2. When the menu is in view, if you start scrolling you’re left with a ‘hanging’ menu. Fugly. This could easily be sent out of view by watching for a scroll/touch event.

I’ll update this post when we ship our first Android app off to the Marketplace with the final version we integrated.

About the author

I'm Sam Croft - a thirtysomething designer/developer and co-founder of Running in the Halls Ltd—a web and app development studio in Huddersfield, UK. I was educated in graphic design and now specialise in front-end web and app development; my main passion being usability and accessibility. I strongly believe web apps (vs native) are the future and love developing for mobile using the wonderful PhoneGap.

I am a massive sports fan - Formula One in particular. I live in the Pennines with my beautiful wife, Alex. Occasionally I own a large scruffy beard.

I tweet about all of my interests - you should follow me. I also have a .

  • Iam4u2c

    Hi Sam – cool website, I like your style – err have you tried Sencha Touch?

  • Anonymous

    Thanks. I have yes, I like it but it was a bit slow on Android and iOS 3.2 <

    The latest release looks very fast though, I'm definitely going to revisit it and try it out again.

  • Guest

    The event has now been changed to “menubutton” instead of “menuKeyDown”

  • Anonymous

    Thanks for the heads up :)

  • http://benholland.me/ Ben Holland

    Cheers Sam. was a great help!! cool site as well

  • Indrajeet

    Hi sam,

    Thanks for such a nice help. Can you provide detailed documentation regarding Developing Android application using PhoneGap and Eclipse.Any sample application which includes Screens with interactive UI.
    I am new in this, so it will benifitial for me and people like me.

    can you help me?

    if you have any such a documents, which you had developed, please try to share it on my mail
    indrajeetdpatil@gmail@gmail:disqus .com

    thanks and regards,
    indrajeet patil

  • http://samcroft.co.uk/ Sam Croft

    Hi Indrajeet. Thanks for your comments.

    I am going to write some articles about Android specific development yes, but when this might be I don’t know. Have you read the getting started guide on the PhoneGap website for Android + Eclipse? http://phonegap.com/start#android – that covers the basics.

    Jonathan Stark also has a fantastic book you can view about developing an Android app using PhoneGap - http://ofps.oreilly.com/titles/9781449383268/ – I recommend checking this out!

  • http://www.tatvasoft.co.uk/android-apps.php Edward Walton

    Very nice coding and very nice post… thanks for sharing this osam post

  • http://samcroft.co.uk/ Sam Croft

    Thank you, you’re most welcome.

  • Jas karan

    Hi Sam,
    This is great. Thanks. Here the UI controls are in HTML.
    Is there any way to embed native android UI controls within the html(index.html) or above it?

    Thanks 
    karan

  • http://samcroft.co.uk/ Sam Croft

    Hi Jas,

    I’m not aware of any native ui plugins for Android projects. I did see this the other day though - https://github.com/mwbrooks/cordova-plugin-menu – looks interesting…

    - Sam