Handling Device Orientation Changes

Handling Device Orientation Changes

In addition to handling differing screen sizes, our apps will likely have to respond to the user changing the orientation of the device. Up until now, we have been designing for our applications being used in portrait mode. While the form we have built will play nicely in landscape mode (as demonstrated in Figure 2–7), it is important to know when the orientation changes, as particular applications you write might need some special treatment.

Figure 2–7. Using relative position makes working with different orientations simple. To demonstrate the techniques for dealing with screen orientation changes, let’s create

a simple page that will provide some feedback as to when orientation changes are occurring.

We will now look at some example code that implements an orientation detection routine that works on both current and previous versions of Android. Additionally, we’ll bring in jQuery here so we can do things a little more concisely. If you aren’t already familiar with jQuery, it is probably worth quickly running through a jQuery tutorial (an extensive list of tutorials can be found at http://docs.jquery.com/Tutorials ), or else the JavaScript that is used in this exercise will appear a little confusing.

36 CHAPTER 2: Building a Mobile HTML Entry Form

First is the page HTML: <html>

<head> <title>Orientation Checker</title> <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-

scale=1.0; user-scalable=0;" /> <link rel="stylesheet" media="screen" href="orientation-monitor.css" /> <script type="text/javascript" src="../../js/jquery-1.4.2.min.js"></script> <script type="text/javascript" src="orientation-monitor.js"></script>

</head> <body>

<h1 class="simple">Orientation Monitor</h1> <ul class="details">

<li class="header">Event Details</li> <li><label>Type:</label><span id="event-type" /></li> <li class="header">Window Details</li> <li><label>Width:</label><span id="window-width" /></li> <li><label>Height:</label><span id="window-height" /></li> <li><label>Orientation:</label><span id="window-orientation" /></li> <li class="header">Detection Results</li> <li><label>Orientation:</label><span id="orientation" /></li> <li><label>Rotation Class:</label><span id="rotation-class" /></li>

</ul> </body> </html>

NOTE: I haven’t included the CSS here for this example, as it isn’t the point of the exercise. You can, however, review the full source at the following URL: http://sidelab.com/code/pawa/snippets/02/orientation-monitor.css

Next is the content of the orientation-monitor.js file: $(document).ready(function() {

var canDetect = "onorientationchange" in window;

var orientationTimer = 0;

var ROTATION_CLASSES = { "0": "none", "90": "right", "-90": "left", "180": "flipped"

$(window).bind(canDetect ? "orientationchange" : "resize", function(evt) {

clearTimeout(orientationTimer); orientationTimer = setTimeout(function() {

// display the event type and window details $("#event-type").html(evt.type); $("#window-orientation").html(window.orientation); $("#window-width").html(window.innerWidth); $("#window-height").html(window.innerHeight);

// given we can only really rely on width and height at this stage,

CHAPTER 2: Building a Mobile HTML Entry Form

// calculate the orientation based on aspect ratio var aspectRatio = 1; if (window.innerHeight !== 0) {

aspectRatio = window.innerWidth / window.innerHeight; } // if

// determine the orientation based on aspect ratio var orientation = aspectRatio <= 1 ? "portrait" : "landscape";

// if the event type is an orientation change event, we can rely on // the orientation angle var rotationText = null; if (evt.type == "orientationchange") {

rotationText = ROTATION_CLASSES[window.orientation.toString()]; } // if

// display the details we have determined from the display $("#orientation").html(orientation); $("#rotation-class").html(rotationText);

Once you have implemented the code, a simulator or device will display a screen similar to what is shown in Figure 2–8.

Figure 2–8. The orientation monitor displaying information for a landscape orientation. Let’s take a walkthrough of the meaningful parts of the preceding code. Firstly, we make

a determination as to whether the version of WebKit that the Android device we are using supports the orientationchange event:

var canDetect = "onorientationchange" in window; Then we use the jQuery bind method to attach ourselves to the relevant event—based

on the previous detection. Here I use the ternary (or elvis) operator to save some keystrokes. (I apologize if you aren’t a fan of Elvis, but I am, so expect to see more of him.)

38 CHAPTER 2: Building a Mobile HTML Entry Form

$(window).bind(canDetect ? "orientationchange" : "resize", function(evt) {

Next, we’ll do something pretty subtle, but very important. As orientationchange and resize events can occur quite rapidly, we need to wait until things have settled down before actually attempting to handle the event. In this case, I am using the JavaScript setTimeout and clearTimeout functions to clear and reset a timer that will run once the event queue has stabilized.

var orientationTimer = 0; clearTimeout(orientationTimer); orientationTimer = setTimeout(function() {

This wraps up the preparatory work that is required to properly capture appropriate events for detecting orientation changes. Let’s now have a look at what is required to interpret the information we are receiving.

First, let’s work out the screen aspect ratio, and from there determine whether the screen is being displayed in portrait or landscape mode:

var aspectRatio = 1; if (window.innerHeight !== 0) {

aspectRatio = window.innerWidth / window.innerHeight; } // if

// determine the orientation based on aspect ratio var orientation = aspectRatio <= 1 ? "portrait" : "landscape";

As I mention in the comments in the full code sample, the preceding code is really the only reliable way at the moment to write detection code that is going to work for most devices. In my testing, an Android device that did not support the orientationchange

Download from Wow! eBook <www.wowebook.com>

event still reported results for the window.orientation value, but always returned 0, regardless of the device’s actual orientation.

Based on the aspect ratio of the display, we can infer the orientation of the screen. We can go a little further with the next section of code to get a value-add for those devices that do actually provide us accurate orientation information:

var ROTATION_CLASSES = { "0": "none", "90": "right", "-90": "left", "180": "flipped"

}; var rotationText = null;

if (evt.type == "orientationchange") {

rotationText = ROTATION_CLASSES[window.orientation.toString()]; } // if

In this code, we first look to see if the event was an orientationchange event. If so, then we take the window.orientation integer value, convert it to a string, and then map it to an array value that we will use in conjunction with CSS classes at a later stage.

CHAPTER 2: Building a Mobile HTML Entry Form

NOTE: As a platform, Android is continuing to evolve and the particular code may have differing effects on different versions of the Android OS. One of the common criticisms of the Android platform relates to the fragmented OS versions available across devices.

To that end, the preceding code worked for Android 1.6, 2.1, and 2.2 in the emulator, but failed to behave correctly on an Android 2.1 device. This is something that is going to prove challenging for Android developers (including web app developers) until Google works with the device manufacturers to ensure Android OS releases are distributed to consumers.

Until that time, it is very important to perform thorough device testing with your apps and be aware of any limitations that particular OS versions may have.

What this code produces for us is essentially two string values that we can utilize for effectively applying stylesheets (or individual styles) for our mobile web apps.

Once we have done this, it’s a very simple matter to tweak the code so that we can use it in future exercises. Essentially, we need to remove the feedback elements from the code (where jQuery is being used to update spans for our example app), and use the jQuery trigger function to fire a new event:

$(window).trigger("reorient", [orientation, rotationText]); Once this is done, you should be able to tweak your code to update the detected

orientation display elements by using jQuery bind to handle “reorient” events. If you run into trouble, just have a look at the final exercise JavaScript code on GitHub:

http://sidelab.com/code/pawa/snippets/02/orientation-monitor.js BUILDING A TOOL SET: We are starting to build up our tool set of reusable code that will be

used in later examples. While I won’t cover the detail of how this is being done, you can have a look at the library at any time. Like the exercise code, the library is available for review at the URL :

http://sidelab.com/code/pawa/js/prowebapps.js