Anatomy of a Cordova application navigation system (part I): swiping gesture

Hey there!

Wellcome to this tutorial about how to build a reliable and effective navigation system for a Cordova mobile application. The little app we’re going to build won’t do anything: no form submission, no database browsing nor picture’s uploading nor sharing functions. Absolutely nothing: its pages will be just empty blank pages, with only a heading tag with the title, just to give us a basic feedback of what’s happening.

What I want to focus on are the tecniques we can use to build an application which supports navigation through its views using gestures (tap and swipe) and hard buttons. Our application will have only one single page, index.html, which we’ll divide in many sections each of which will represent an independent view.

Cordova application navigation system

Features

Our app will be structured on two types of views:

  • some views will be grouped and they will be the main views of our app: we’ll be able to navigate through these views by swiping our finger to the left or to the right or alternatively tapping a button in the top bar; to go to the previous view we’ll also be able to use the hard back button of our phone
  • other views will be independent each other and these will be the secondary views: to this second type of views will belong a side menu and all those views used by the application to perform specific task, like adding a customer, register a new user, see details of a product and so on (hey, keep in mind these are just example and we won’t implement any of these specific functions in our example application

We’ll discuss following aspects of the navigation system:

  • how to implement a swiping system to navigate through the main views: swiping to the left we’ll be able to go to the next view, swiping to the right we’ll go back to the previous one.
  • how to build a fixed top menu with icons to provide our users an alternative way to navigate through the main views
  • how to create a side menu
  • how to build several views to do specific actions: these secondary views will be called by the menu items or by buttons placed in the main vews
  • how close the secondary views using a soft back button, a swipe gesture or the phone back button indifferently
  • how to use the phone back button to go back to the previous page, no matter if it is a main or a secondary view or a side menu;  we’ll also implement the standard behavior of the back button so if the user taps it twice it will exit the app

Requisites

To build our navigation system we’re going to use some usual tool, wich any web develper is familiar with, and some additional plugin:

  • Bootstrap 3.x
  • jQuery
  • jquery.touchSwipe.js (it’s not a Cordova plugin but it works great even in Cordova applications)
  • PhoneGap Toast plugin (this is not a requirement for the navigation system but it’s nice to show messages to the user in perfect Android style instead of using javascript alerts)

We’ll proceed to install each plugin when we’ll need it, so for the moment don’t do anything: just keep reading on, the time of action will arrive soon!

Before you go on reading the tutorial be sure to having followed the steps described in the article Basic Cordova project setup. I admit it’s not a great article, but it gives some basic suggestion to start a new Cordova project and make it ready to follow the various tutorials you’ll find in this blog. The only change you have to do is the application name; use this command: cordova create navApp com.codingfix.navApp NavApp . For the rest just follow the steps described in that article: not because they represent the best way to setup a Cordova project but just because this way we will be sure our files are identical and we won’t risk some boring misunderstanding.

The main views

Now, we’re ready to start. The first thing we’re going to build is the 4 main views structure of the application, so we make it ready to support swiping gesture.

The markup

After the body tag add following markup:

<div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="false">
	<div class="carousel-inner" role="listbox">
		<div id="page1" class="item active"><h2>Page 1</h2></div>
		<div id="page2" class="item "><h2>Page 2</h2></div>
		<div id="page3" class="item"><h2>Page 3</h2></div>
		<div id="page4" class="item"><h2>Page 4</h2></div>
	</div>
</div>

The Bootstrap carousel is a great and easy way to implement the gesture support in a hybrid mobile app. But before we can see it in action we have to do some additional things.

First of all, we have to install the TouchSwipe plugin. You can use npm or bower or you can just download or clone the repo from Github: https://github.com/mattbryson/TouchSwipe-Jquery-Plugin.

Once you have the software installed, put the minified version of the plugin, the file jquery.touchSwipe.min.js in your navApp/www/js/ directory and link it in your index.html file:

        <script type="text/javascript" src="cordova.js"></script>
		<script type="text/javascript" src="js/jquery-2.2.3.min.js"></script>
		<script type="text/javascript" src="js/bootstrap.min.js"></script>
		<script type="text/javascript" src="js/jquery.touchSwipe.min.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

The stylesheet

Open your navApp/www/css/index.css using your code editor of choice. If you followed my suggestions it should look like this:

html, body{
	height: 100%;
}

Now, add following lines:

.carousel,.item,.active{
	height:100%;
}
.carousel-inner{
	height:100%;
}

With this our pages will fill out the available space in our smartphone display.

The javascript

Now that we have everything in place, we just have to write a few lines of code to setup our carousel and initialize TouchSwipe plugin. First of all, within the jQuery main function we have in our navApp/js/index.js, we have to initialize Bootstrap carousel:

$(document).ready(function () {
	$('.carousel').carousel('pause');
});

Do you see? We pause carousel because we don’t want our application views automatically slide like it were pictures in a website!

Then we go with the code for the TouchSwipe plugin:

$(".carousel-inner").swipe({
	swipeLeft: function () {
		$(this).parent().carousel('next');
	},
	swipeRight: function () {
		$(this).parent().carousel('prev');
	},
	threshold: 200,
	excludedElements: "label, button, input, select, textarea, .noSwipe"
});

Notice we’re binding the event swipe to the element with class carousel-inner. The swipe event has 2 callback functions attached in our exemple, swipeLeft and swipeRight: accordingly to the triggered event, we’ll use Bootstrap to make our carousel slide to the next item for swipeLeft and to the previous one for swipeRight. Then we set threshold to 200: this means that the user has to swipe his finger for at least 200 px in order the swipe event be triggered. Finally we exclude some element we don’t want trigger the swipe event.

It’s far beyond the scope of this tutorial to analyze the many options which TouchSwipe pluginoffers to you to manage gestures. Maybe this will be the argument for another article. Anyway, if you want to know better this incredibly helpful plugin, you can read the full documentation here.

Go!

Okay, it’s the moment to check if we have done everything the right way. Connect your phone to your computer, open your CLI and go to your project directory navApp/ and type the following command:

cordova run android

Wow, fantastic! You can navigate your views just swiping your finger to the left or to the right!!! Great! But… Whooops! It’s never stopping! It is sliding again and again, in an infinite loop: that’s bad. As any other mobile application, once you have reached the last view, the swipe event to the left shouldn’t be triggered anymore; and when you are on the first view it couldn’t be possible swiping to the right and go directly to the view 4.

Okay, we’ll have to fix this bug. Fortunately, Bootstrap has a vast arsenal ready for us and among the other weapons we can find the event slide.bs.carousel. By binding this event to your carousel you can execute custom code before the sliding occur (to execute some code after the slide event has been fired use slid.bs.carousel instead).

So it’s the moment to use the ids we have setup for our views. We’re going to use them to check if the current page  is page1 or page4 and acting accordingly just preventing the default event behavior:

$('#myCarousel').on('slide.bs.carousel', function (e) {
	var page = $('.item.active').attr('id');
	if (e.direction == 'left') {
		if (page == 'page4') {
			e.preventDefault();
		}
	} else {
		if (page == 'page1') {
			e.preventDefault();
		}
	}
});

Probably someone could think we could make this code more compact and he would be right. But I prefer to keep it this way just for a reason: in a real app, we’ll probably have to execute specific lines of code accordingly not only to the swipe direction but even to the active page id, so keeping the check for the direction separated from the check of the page is more comfortable and it makes immediately clear how to manage this aspect of our app.

That’s all right, now. Run the app again and you’ll see that it behaves as expected. Your whole index.js should like this:

var app = {
    initialize: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
    },
    onDeviceReady: function() {
        this.receivedEvent('deviceready');
    },
    receivedEvent: function(id) {
        console.log('Received Event: ' + id);
    }
};

app.initialize();

$(document).ready(function () {
	$('.carousel').carousel('pause');

	$(".carousel-inner").swipe({
		swipeLeft: function () {
			$(this).parent().carousel('next');
		},
		swipeRight: function () {
			$(this).parent().carousel('prev');
		},
		threshold: 200,
		excludedElements: "label, button, input, select, textarea, .noSwipe"
	});
	
	$('#myCarousel').on('slide.bs.carousel', function (e) {
		var page = $('.item.active').attr('id');
		if (e.direction == 'left') {
			if (page == 'page4') {
				e.preventDefault();
			}
		} else {
			if (page == 'page1') {
				e.preventDefault();
			}
		}
	});
	
});

The hard back button

Usually, tapping the hard back button of your phone you get brought to the previous app view: wouldn’t it wonderful if we could implement this feature in our app? Yes, it’d be. So open you index.js file and look for the onDeviceReady method of the app object (if you have followed my suggestions, it should be at line 5). Change this method making it look like the following:

onDeviceReady: function () {
	this.receivedEvent('deviceready');
	document.addEventListener("backbutton", onBackKeyDown, false);
},

This way, everytime the hard back button of our phone will be tapped, Cordova will fire the function onBackKeyDown(). We can keep this function quite simple, for the moment:

function onBackKeyDown(e) {
	e.preventDefault();
	$('.carousel').carousel('prev');
}

If you run your app, you can see this code works fine. But, since we have used the jQuery preventDefault() method to manage the back button by ourselves, now we are no more able to exit the app. So we have to make our onBackKeyDown() function a bit more complex. Before to see the code, let’s install another great plugin, the PhoneGap Toast plugin. Go to your CLI and, in your project root directory, type the following command:

cordova plugin add https://github.com/EddyVerbruggen/Toast-PhoneGap-Plugin.git

This plugin will allow us to show some alert to our users using a native Android style. Come back to the code.

var lastTimeBackPress = 0;
var timePeriodToExit = 2000;
function onBackKeyDown(e) {
	e.preventDefault();
	var page = $('.item.active').attr('id');
	if (page == 'page1') {
		if (new Date().getTime() - lastTimeBackPress < timePeriodToExit) {
			navigator.app.exitApp();
		} else {
			window.plugins.toast.showWithOptions({
				message: "Press again to exit.",
				duration: "short", // which is 2000 ms. "long" is 4000. Or specify the nr of ms yourself.
				position: "bottom",
				addPixelsY: -40  // added a negative value to move it up a bit (default 0)
			});
			lastTimeBackPress = new Date().getTime();
		}
	} else {
		$('.carousel').carousel('prev');
	}
}

The first thing we notice is the couple of variables lastTimeBackPress and timePeriodToExit. We’ll use these variables to check if the user has tapped twice on the back button and to measure the time between the first and the second tap: if this time is lower than 2 seconds we exit the app otherwise we show a “toast” to the user telling him he has to tap again to exit the app. Toast plugin’s options are just a few and they are absolutely self-explanatory, so I’m not going to bore you with unnecessary bla-bla-bla. Just run the app, swipe to the left and to the right to navigate between the views, use the phone back button to go back to the first view and tap the back button again to see your “toast”. Cool, isn’t it?

In the next article we’ll see how to add a fixed top bar to increase the navigation options for our users.

Cheers 🙂


Part 1Part 2Part 3Part 4


 

Leave a Comment

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