Chapter 4. Customizing Your App for Mobile

So far we have gotten our example app onto a mobile device, but that doesn’t really make it ready for mobile. Mobile devices are different than desktop computers. Not only do they have smaller screens but they are also usually touch driven. Fingers are bigger than a mouse cursor. Certain actions, like scrolling, are easier to do with a finger, whereas other actions like opening drop down menus are harder or impossible to do without a mouse.

Finally, mobile devices typically have a slower network connection and less processing power than a full blown desktop or laptop. You must take all of these complications into account in order to create a great mobile experience, and that is before we even consider mobile specific features like GPS and accelerometers.

In this section we will take a look at the ways mobile devices are different and how to adapt content and applications to these new constraints.

CSS Overview

Fundamentally GWT builds apps with the technology of web pages. Even though we almost never have to touch the HTML markup directly, GWT is using HTML, JavaScript, and CSS underneath. Most modern web sites use HTML only to represent the content of a page or application. The visuals and styling are controlled using a separate document called a Cascading Style Sheet, or CSS file. A CSS file controls the drawing of each element on the screen. It controls both how it is drawn: the colors, backgrounds, fonts, and effects; as well as where it is drawn: the layout and sizing. CSS is a powerful technology that can completely transform HTML content for different target devices.

As a quick review, here is what CSS looks like:

div {
    font-size: 200%;
}

div.foo {
    color: blue;
}

A CSS file consists of a list of rules. Each rule has two parts: the selector and the properties. The selector determines what the rule affects. The selector div will apply the rule only to div elements. A selector of div.foo will apply the rule only to divs with a css class of “foo”. For example:

<div> not affected by div.foo </div>
<div class="foo"> *IS* affected by div.foo </div>

The second part of a CSS rule is the list of property settings. A property is some attribute of an element, such as its color, font, width, or background image. Different elements support different properties and in different ways, though mostly this will be hidden for us by GWT.

The combination of selectors and properties let us style any element in any way we choose with a very compact syntax. For example: to color all text inside of a bold element b with red and give it a solid one-pixel black border, we can use this style:

b {
    color: red;
    border: 1px solid black;
}

Styling GWT with CSS

So how does this apply to GWT? Remember that every GWT widget ultimately becomes an element in a web page, usually a DIV. GWT helpfully adds CSS style classes to every widget based on that widget’s Java class name. With this style class we can hang any style we want.

Suppose we want to make every text field in the app have a thick green border. We can do this with the following style:

.gwt-TextBox {
  border: 5px solid green;
}

Now that we have the mechanics of CSS working, let’s use CSS to restyle PerchSearch from last chapter.

To demonstrate the power of CSS I added a bit more to the UI of the PerchSearch app from the previous chapter. I added a header at the top and a navigation bar on the side. If I load it directly onto a 1024×768 tablet, it looks like Figure 4-1.

Functional but ugly default UI
Figure 4-1. Functional but ugly default UI

The basic UI is functional but ugly. Fortunately we can easily add some style. First I added style names to the header, sidebar, and labels for each tweet using the addStyleName method on the widgets.

//in the init code
header.addStyleName("header");
...
nav.addStyleName("nav");
...
//in the tweet handler onSuccess callback
label.addStyleName("tweet");

Now we can hang some style on the UI. I want the header to be larger and centered. The navigation buttons should be plain blue with white text instead of the standard button look. The actual tweet text should be bigger with a more spacing and subtle border. To do all of this I added the following code to the PerchSearch.css file in the war directory.

.header {
    font-size: 200%;
    text-align: center;
}

.nav .gwt-Button {
    width: 100%;
    margin: 3px 0px 3px 0px;
    color: white;
    background: none;
    background-color: rgb(160,200,250);
    border: 0px solid black;
}


.tweet {
    color: black;
    padding: 0.3em;
    font-size: 130%;
    margin: 2px;
    border: 1px solid #e0e0e0;
    background-color: #fafafa;
}

Notice the buttons are styled with the selector .nav .gwt-Button. This is a compound selector. It means that the properties will only affect elements marked with gwt-Button that are also inside of an element marked with nav. This restricts the style changes to only the navigation buttons. The button next to the text field won’t be affected.

The final result is in Figure 4-2.

The reskinned interface
Figure 4-2. The reskinned interface

Adapting to Device Screen Sizes

The next consideration for any mobile app is screen size. Mobile devices have smaller screens with higher DPI than desktop monitors and they are usually viewed closer to the eye than a desktop. To account for this the web browser on most smart phones assumes a screen width of 960 pixels rather than the actual pixel width of the device (for example, 320px in portrait mode on the original iPhone). The browser allows the user to zoom in and out of the screen dynamically. This is great for random pages on the web but for a mobile app you want greater control over how the user sees your content.

The first adjustment we can make is to force the page to render at the same size as the physical screen. We can do this by adding a meta tag to the head section of the page. For a GWT app this means adding the following to the war/PerchSearch.html file.

<meta name="viewport" content="width=device-width; initial-scale=1.0;
      user-scalable=no;" />

By setting width=device-width it will tell the browser to make one pixel on your page equal one real pixel on the device. The third command, user-scalable=no, disables user scaling. Since we have the page at exactly the right width the user shouldn’t have to scale. Instead we will make our text the right size for a mobile device. This only disables scaling, though. The user can still pan around. However, as long as we make sure there is no content sticking off the edge page the user will only need to pan up and down, which is the easiest gesture for a mobile device with a touch screen.

So far we have restyled PerchSearch to run on a tablet at 1024×768, which is close enough to a desktop to work in both places. However, if we put it on a standard iPhone in portrait mode then we will have only 320 pixels across to work with. It will look like Figure 4-3.

PerchSearch running on iOS
Figure 4-3. PerchSearch running on iOS

Not horrible but the sidebar really takes away from the tweets. We could change the fonts and layout to work on the phone, but then it would look wrong on the tablet again. We need a way to specify style for each device, preferably without hard coding it to specific brands of devices.

Fortunately CSS has a solution: media selectors. You can designate a block of CSS rules to apply only to a particular size of screen. For example, if we want to make the fonts smaller on a phone but larger on a tablet we can use this CSS.

@media screen and (max-width: 1024px) {
    .header {
        font-size: 200%;
    }
}
@media screen and (max-width: 320px) {
    .header {
        font-size: 120%;
    }
}
.header {
    text-align:center;
}

The beauty of this approach is that the browser will ignore anything that doesn’t match the current device. If you are on a phone then the rules in the 320px block will override the rules from the 1024px block. If you are on a tablet then you will get the 1024px block and the 320px block will be completely ignored. The text-align rule is not inside any block so it will always apply.

CSS media queries support conditions based on width, height, orientation, aspect ratio, and other device features. (See the full CSS spec here for details http://www.w3.org/TR/css3-mediaqueries/ ). Using media queries lets you completely customize the look of your app for each device size without changing your actual app code at all, and again it degrades gracefully on older devices.

Adjusting Layout for Mobile

Now that leads us into the next problem: layout. It is very common to create websites which have two or three columns. The left and right columns usually have navigation or sidebars while the center column contains the same information. Squishing this onto a mobile screen is never going to look good, as we can see on the iPhone screenshot. The user will either have to zoom out to see everything or pan to the left and right to see the navigation.

A better solution is to change the layout dynamically to fit the device. You can use the traditional three column layout on a desktop, but switch to two columns (nav + content) for a tablet and one column on a phone, putting the navigation and sidebar above or below the content. Thanks to CSS media selectors we can do this entirely with our style sheets, not changing the app code at all.

To adjust PerchSearch for a phone sized device we will make it fit into one column by moving the navigation to be between the header and content. To not waste vertical space we will also make the navigation be horizontal. To determine which CSS to use we need to know what markup GWT has generated for us.

If you use a desktop web browser with a page inspector like Chrome you can see the generated markup. The markup for the main DockLayoutPanel of PerchSearch looks like this:

<div style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; "><div style="position: absolute; z-index: -32767; top: -20ex; width: 10em; height: 10ex; ">&nbsp;</div><div style="position: absolute; overflow-x: hidden; overflow-y: hidden; left: 0px; top: 0px; right: 0px; bottom: 0px; "><div style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; " class="dock"><div style="position: absolute; z-index: -32767; top: -20ex; width: 10em; height: 10ex; ">&nbsp;</div><div style="position: absolute; overflow-x: hidden; overflow-y: hidden; left: 0em; top: 0em; right: 0em; height: 3em; "><div class="gwt-Label header" style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; ">PerchSearch</div></div><div style="position: absolute;   left: 0em; top: 3em; bottom: 0em; width: 5em; "><div class="nav" style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; "><button type="button" class="gwt-Button">All</button><button type="button" class="gwt-Button">Puppies</button><button type="button" class="gwt-Button">Lol Cats</button><button type="button" class="gwt-Button">Clear</button></div></div><div style="position: absolute; overflow-x: hidden; overflow-y: hidden; left: 5em; top: 3em; right: 0em; bottom: 0em; "><table cellspacing="0" cellpadding="0" style="position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; "><tbody><tr><td align="left" style="vertical-align: top; "><input type="text" class="gwt-TextBox"></td></tr><tr><td align="left" style="vertical-align: top; "><button type="button" class="gwt-Button">Search Twitter</button></td></tr><tr><td align="left" style="vertical-align: top; "><div class="gwt-Label"></div></td></tr><tr><td align="left" style="vertical-align: top; "><table cellspacing="0" cellpadding="0"><tbody></tbody></table></td></tr></tbody></table></div></div></div></div>

Uh oh. That’s not good. DockLayoutPanel generates many extra divs with lots of inline styles. That’s going to be very hard to override with CSS. We need another solution.

To tackle this problem we will use a technique called “tags first GWT”. For some applications you don’t care about the markup and can use whatever GWT generates. For other applications, like mobile ones, we want to specify some of the markup first, then tell GWT to work with what we created. GWT does this very easily using the RootPanel.

Normally we call RootPanel.get().add(panel) to add a panel to the root of the page. RootPanel optionally lets us specify an id of the HTML element we want to use instead of defaulting to the root of the page. With this technique we can insert GWT markup inside of our own custom page.

Let’s start by modifying the main app page in war/PerchSearch.html. Previously the body was empty. Now let’s create our own set of divs, one for each major chunk of the app.

<div id="dock_container">
    <div id="header_container"></div>
    <div id="nav_container"></div>
    <div id="content_container"></div>
</div>

Now modify the Java code to use these nice clean divs. First I removed all references to the dock panel.

Label header = new Label("PerchSearch");
header.addStyleName("header");

FlowPanel nav = new FlowPanel();
nav.add(new Button("All"));
nav.add(new Button("Puppies"));
nav.add(new Button("Lol Cats"));
nav.add(new Button("Clear"));
nav.addStyleName("nav");

VerticalPanel panel = new VerticalPanel();
panel.addStyleName("content");
queryField.setText("puppy");
panel.add(queryField);
final Button searchButton = new Button("Search Twitter");
panel.add(searchButton);
final Label errorLabel = new Label();
panel.add(errorLabel);
panel.add(resultsPanel);

Now I can add the header, panel, and nav to the page specifically where I want them using RootPanel.get:

RootPanel.get("header_container").add(header);
RootPanel.get("nav_container").add(nav);
RootPanel.get("content_container").add(panel);

The app now has nice clean markup but no layout at all; just some unstyled divs. Now we can jump back into the CSS. For the desktop version we want the header on top, the navigation on the left, and the content in the center adjacent to the nav and below the header.

#header_container {
}
#nav_container {
    position: absolute;
    width: 10em;
    left: 0px;
    top: 4em;
}
#content_container {
    position: absolute;
    left: 11em;
    top: 4em;
}

I left the header alone since it looks fine. For the nav and content containers I made them be absolutely positioned then hard coded a width, left, and top value. The navigation will be 10 ems wide and 4 em from the top of the page to make room for the header. The content_container div will be 11ems from the left to make room for the navigation, and also 4 ems down. Notice that I’m using em’s instead of pixels. This makes sure the page scales nicely with the user’s preferred font.

The above CSS is now the default. When the screen is narrower than 480px we want the layout to change. That’s what this CSS does:

@media screen and (max-width: 480px) {
    #nav_container {
        width: 100%;
    }
    #content_container {
        top: 7em;
        left: 0em;
    }
    .nav .gwt-Button {
        width: auto;
        margin: 0.1em 0.5em 0.1em 0.5em;
    }

}

If the screen is narrower then the navigation will stretch completely across the screen using width:100%. The content moves further down and all the way to the left using top: 7em and left:0em. We also set the width of the navigation buttons to auto. Before they were 100% wide, meaning they would each be as wide as the screen. Instead we want them to be sized automatically based on the text in them, which is what auto does. I also tweaked the margins a tiny bit to make them look prettier. The final results on a phone look like Figure 4-4.

PerchSearch using the tags first approach.
Figure 4-4. PerchSearch using the tags first approach.

In all of this talk about screen size we have assumed that a particular device always has the same size but that’s not always true. Many devices can actually be rotated using an accelerometer, which is the device equivalent of resizing the browser window. You can create conditional styles to deal with this by sticking with the width media selector. The width will usually be updated when the user rotates the device. If you want to do something special just for portrait or landscape mode, rather than depending on the screen width you can do it with these media selector:

@media all and (orientation:portrait) {
}
@media all and (orientation:landscape) {
}

CSS is incredibly powerful. It can restyle almost anything on the page using a very simple syntax and degrades gracefully. All web renderers will ignore any CSS properties they don’t understand. This lets you add advanced features like animations for recent browsers and devices but still fall back gracefully to standard features on less sophisticated browsers.

Thanks to our tags first approach we can completely restyle the app without modifying code at all. This did require a bit of extra work to set up the tags , however. In the next chapter we will take a different approach to making apps ready for mobile.

Get Building Mobile Applications with Java now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.