If you know HTML, CSS, and JavaScript, you already have the tools you need to develop Android applications. This hands-on book shows you how to use these open source web standards to design and build apps that can be adapted for any Android device -- without having to use Java. Buy the print book or ebook or purchase it in iBooks. |
Ultimately, we are going to build a native Android app using HTML, CSS, and JavaScript. The first step on this journey is to get comfortable styling HTML to look like a mobile app. In this chapter, I’ll show you how to apply CSS styles to a bunch of existing HTML pages so that they are easily navigable on an Android phone. So, in addition to moving closer to building a native app, you’ll be learning a practical (and valuable) skill that you can use immediately.
If you’ve been testing all your web pages locally on your personal computer, you won’t be able to view them on your Android phone without setting up a server. You have a couple choices:
Host your web pages on a web server and connect to that server from your Android phone. Chances are good that your Internet Service Provider (ISP) offers complimentary web hosting, but this usually only supports basic features such as HTML. By the time we get to Chapter 6, Going Offline, we’re going to need to use PHP, a scripting language that runs on the web server, so you should look into an inexpensive hosting service. Companies such as Laughing Squid (http://laughingsquid.us/) offer entry-level hosting with PHP for under $10 a month.
Host them on a web server running on your computer, and connect to the web server running on your computer from your Android phone. This only works when your Android phone and computer are on the same Wi-Fi network.
This chapter is set up so you can try the examples as you go through it. So no matter which option you choose for viewing the web pages, try reloading them in a browser (preferably the Android browser) each time you add something new to one of the samples. However, be sure to save your file in your text editor before you reload it in the browser or you won’t see your changes.
Theory is great, but I’m a “show me, don’t tell me” kinda guy so let’s dive in.
Imagine that you have a website that you want to "mobileize" (Figure 2.1, “Desktop version of a typical website looks fine in Chrome on a computer.”). In this scenario, there are a number of easy things you can do to optimize a site for Android. I’ll go over your options in this chapter.
Figure 2.2, “Desktop version of a typical website looks alright on an Android phone, but we can do a lot better.” shows what this web page looks like on the Android phone. It’s usable, but far from optimized for Android.
Figure 2.2. Desktop version of a typical website looks alright on an Android phone, but we can do a lot better.
Example 2.1, “The HTML document we’ll be styling.” shows an abbreviated version of the website shown above. This is the HTML you’ll be working with in this chapter. You can download it from the book's website (see the section called “How to Contact Us”) if you'd like to try styling it as you go through the chapter. The desktop style sheet (screen.css
is not shown as it is not essential, but you can use the style sheet from the previous chapter if you'd like to have something to play with).
Example 2.1. The HTML document we’ll be styling.
<html> <head> <link rel="stylesheet" href="screen.css" type="text/css" /> <title>Jonathan Stark</title> </head> <body> <div id="container"> <div id="header"> <h1><a href="./">Jonathan Stark</a></h1> <div id="utility"> <ul> <li><a href="about.html">About</a></li> <li><a href="blog.html">Blog</a></li> <li><a href="contact.html">Blog</a></li> </ul> </div> <div id="nav"> <ul> <li><a href="consulting-clinic.html">Consulting Clinic</a></li> <li><a href="on-call.html">On Call</a></li> <li><a href="development.html">Development</a></li> <li><a href="http://www.oreilly.com">O'Reilly Media, Inc.</a></li> </ul> </div> </div> <div id="content"> <h2>About</h2> <p>Jonathan Stark is a web developer, speaker, and author. His consulting firm, Jonathan Stark Consulting, Inc., has attracted clients such as Staples, Turner Broadcasting, and the PGA Tour. ... </p> </div> <div id="sidebar"> <img alt="Manga Portrait of Jonathan Stark" src="jonathanstark-manga-small.png"/> <p>Jonathan Stark is a mobile and web application developer who the Wall Street Journal has called an expert on publishing desktop data to the web.</p> </div> <div id="footer"> <ul> <li><a href="services.html">Services</a></li> <li><a href="about.html">About</a></li> <li><a href="blog.html">Blog</a></li> </ul> <p class="subtle">Jonathan Stark Consulting, Inc.</p> </div> </div> </body> </html>
For years, web developers used tables to lay out elements in a grid. Advances in CSS and HTML have rendered that approach not only obsolete, but undesirable. Today, we primarily use the div element (along with a variety of attributes) to accomplish the same thing, but with more control. Although a complete explanation of div-based layouts is well outside the scope of this book, you'll see plenty of examples of it as you read through the book. To learn more, please check out Designing with Web Standards by Jeffrey Zeldman which covers the issue in greater detail.
I’m as DRY as the next guy, but in the real world you’re better off making a clean break between your desktop browser stylesheet and your Android stylesheet. Take my word for it and just make two completely independent files; you’ll sleep better. The alternative would be to wedge all of your CSS rules into a single stylesheet, which ends up being a bad idea for a number of reasons, the most obvious of which is that you’d be sending a bunch of irrelevant desktop style rules to the phone, which is a waste of precious bandwidth and memory.
DRY stands for “Don’t repeat yourself,” and is a software development principle that states that “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” The term was coined by Andrew Hunt and David Thomas in their book The Pragmatic Programmer.
To specify a stylesheet specifically for Android, replace the stylesheet link tag in the sample HTML document with ones that use the following expressions:
<link rel="stylesheet" type="text/css" href="android.css" media="only screen and (max-width: 480px)" /> <link rel="stylesheet" type="text/css" href="desktop.css" media="screen and (min-width: 481px)" />
I specifically used max-width
and min-width
here so that you can resize your desktop browser and see the mobile version of the page. If you would prefer to serve the desktop.css
stylesheet to desktop users regardless of their browser window size, use max-device-width
and min-device-width
instead.
The Wireless Universal Resource File (WURFL) contains information that you can use to identify a huge number of wireless devices, including Android devices. If you need to detect Android devices with a width greater than 480 (such as a tablet), or if you don't want the mobile version of the site to appear when users resize their browser window below 480, you can use WURFL's PHP API to precisely detect specific browsers. See Appendix A, Detecting Browsers with WURFL for more information on WURFL.
Here, desktop.css
refers to whatever your existing desktop stylesheet is, and android.css
is a new file that we’ll be discussing in detail in a bit. The desktop.css
file is not essential, but you can use the style sheet from the previous chapter if you'd like.
If you're following along using the sample HTML document shown earlier, you'll now need to rename screen.css
to desktop.css
, but since we're focused on the Android style sheet, you can ignore the desktop style sheet completely. If it fails to load, your browser won’t get too upset.
However, if you'd like to use Chrome to test out the Android-optimized version of the site, you should replace the reference to desktop.css
with a reference to android.css
. That way, you'll get to run the Android version of your site whether you load it from a phone or the desktop browser.
Regrettably, Internet Explorer will not understand these expressions, so we have to add a conditional comment (shown in bold) that links to the desktop version of the CSS:
<link rel="stylesheet" type="text/css"
href="android.css" media="only screen and (max-width: 480px)" />
<link rel="stylesheet" type="text/css"
href="desktop.css" media="screen and (min-width: 481px)" />
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="explorer.css" media="all" />
<![endif]-->
So now it’s time to edit the HTML document if you haven't already done that as you were following along: delete the existing link
to the screen.css
CSS file, and replace it with the lines just shown. This way, you will have a clean slate for the Android-specific CSS that I’ll show you in this chapter.
Unless you tell it otherwise, the Android browser is going to assume that your page is 980px wide (Figure 2.3, “Android assumes a normal web page is 980px wide”). In the majority of cases, this works great. However, you are going to format our content specifically for the smaller dimensions of the Android phone, so you must let the mobile browser know about it by adding a viewport meta tag to the head
element of the HTML document:
<meta name="viewport" content="user-scalable=no, width=device-width" />
The viewport meta tag will be ignored by desktop browsers, so you can include it without worrying about the desktop version of your site.
If you don’t set the viewport width, the page will be zoomed out when it first loads. It’s tough to say exactly what the zoom level will be because the Android browser includes a setting that allows users to set their default zoom. The options are Far, Medium (the default), or Close. Note that even if you do set the viewport width, these user defined settings will affect the zoom level of your app.
Figure 2.4. Setting the viewport to the width of the device make your pages a lot more readable on Android.
Merely by suppressing the desktop stylesheet and configuring your viewport, you will have already given your Android users an enhanced experience (Figure 2.4, “Setting the viewport to the width of the device make your pages a lot more readable on Android.”). To really impress them, let’s start building the android.css
stylesheet.
There are a number of user interface (UI) conventions that make an Android app look like an Android app. In the next section, I’ll add the distinctive title bar, lists with rounded corners, finger friendly links that look like glossy buttons, etc. With the text editor of your choice, create a file named android.css
and add the following to it, then save the file in the same directory as your HTML document:
Example 2.2. Setting some general site-wide styles on the HTML body element.
body { background-color: #ddd; /* Background color */ color: #222; /* Foreground color used for text */ font-family: Helvetica; font-size: 14px; margin: 0; /* Amount of negative space around the outside of the body */ padding: 0; /* Amount of negative space around the inside of the body */ }
Note that all text on Android is rendered using a custom font named Droid. The Droid font family was specifically built for mobile devices, has excellent character set support, and contains three variants: Droid Sans, Droid Sans Mono and Droid Serif. Therefore, specifying a font family of Helvetica as I've done here will only have an effect on devices other than Android.
Now, I’ll attack the header div that contains the main home link (i.e. the logo link) and the primary and secondary site navigation. The first step is to format the logo link as a clickable title bar. Add the following to the android.css
file:
#header h1 { margin: 0; padding: 0; } #header h1 a { background-color: #ccc; border-bottom: 1px solid #666; color: #222; display: block; font-size: 20px; font-weight: bold; padding: 10px 0; text-align: center; text-decoration: none; }
I’m going to format the primary and secondary navigation ul
blocks identically, so I can just use the generic tag selectors (i.e. #header ul
) as opposed to the tag ids (i.e. #header ul#utility, #header ul#nav
):
#header ul { list-style: none; margin: 10px; padding: 0; } #header ul li a { background-color: #FFFFFF; border: 1px solid #999999; color: #222222; display: block; font-size: 17px; font-weight: bold; margin-bottom: -1px; padding: 12px 10px; text-decoration: none; }
Figure 2.5. A little bit of CSS can go a long way toward enhancing the usability of your Android app.
Pretty simple so far, right? With this little bit of CSS, we have already made a big improvement on the Android page design (Figure 2.5, “A little bit of CSS can go a long way toward enhancing the usability of your Android app.”). Next, add some padding to the content and sidebar divs to indent the text from the edge of the screen a bit (Figure 2.6, “Indenting text from the edges.”):
#content, #sidebar { padding: 10px; }
You might be wondering why I added padding to the content and sidebar elements instead of setting it globally on the body element itself. The reason is that it’s very common to have elements that you want to have displayed edge to edge (as with the header in this example). Because of this, padding applied to the body or some other element that's wrapped around lots of others can become more trouble than it’s worth.
The content in the footer of this page is basically a rehash of the navigation element (the ul
element with the id nav
) at the top of the page, so you can remove the footer from the Android version of the page by setting the display to none.
#footer { display: none; }
Time to get a little fancier. Starting from the top of the page, add a 1 pixel white drop shadow to the header text, and a CSS gradient to the background:
#header h1 a { text-shadow: 0px 1px 1px #fff; background-image: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#999)); }
If you are not familiar with the text-shadow
declaration, the parameters from left to right are: horizontal offset, vertical offset, blur, and color. Most of the time, you’ll be applying the exact values shown here to your text because that’s what usually looks good on Android, but it is fun to experiment with text-shadow because it can add a subtle but sophisticated touch to your design.
On most browsers, it’s fine to specify a blur radius of 0px. However, Android requires you to specify a blur radius of at least 1px. If you specify a blur of 0, the text shadow will not show up on Android devices.
The -webkit-gradient
line deserves special attention. It’s an instruction to the browser to generate a gradient image on the fly. Therefore, a CSS gradient can be used anywhere you would normally specify a url()
(e.g. background image, list style image). The parameters from left to right are as follows: the gradient type (can be linear or radial), the starting point of the gradient (can be left top, left bottom, right top, or right bottom), the end point of the gradient, the starting color, and the ending color.
Note that you cannot reverse the horizontal and vertical portions of the four gradient start and stop point constants (i.e. left top, left bottom, right top, or right bottom). In other words, top left, bottom left, top right, and bottom right are invalid values.
The next step is to add the traditional rounded corners to the navigation menus.
#header ul li:first-child a { -webkit-border-top-left-radius: 8px; -webkit-border-top-right-radius: 8px; } #header ul li:last-child a { -webkit-border-bottom-left-radius: 8px; -webkit-border-bottom-right-radius: 8px; }
As you can see, I’m using corner-specific versions of the -webkit-border-radius
property to apply an 8 pixel radius to both the top two corners of the first list item, and the bottom two corners of the last list item (Figure 2.7, “Gradients, text shadows, and rounded corners start to transform your web page into a native looking Android app.”).
It would be cool if you could just apply the border radius to the enclosing ul
, but it doesn’t work. If you try it you’ll see that the square corners of the child list items will overflow the rounded corners of the ul
, thereby negating the effect.
Figure 2.7. Gradients, text shadows, and rounded corners start to transform your web page into a native looking Android app.
Technically, I could achieve the rounded list effect by applying the radius corners to the ul
if I set the background color of the ul
to white, and set the background of its child elements to transparent. However, when you click the first or last items in the list, the tap highlight will show up squared off and it looks terrible. Your best bet is to apply the rounding to the a tags themselves as I’ve demonstrated here.
The occurrences of :first-child
and :last-child
above are called pseudo classes. Pseudo classes are a special type of CSS selector that allow you to target elements that meet certain implicit contextual criteria. In other words, you can style things based on stuff like where they are in a list, whether they have cursor focus, or if they have been clicked, without having to manually update your markup. For example, li:first-child
will select the first li
that is the child of its ul
parent. Without the code pseudo class, I’d have to manually add a class to the first li
to let the browser know that it was the first one.
My next step is to add some JavaScript to my page to support some basic dynamic behavior. In particular, I want to allow the user to show and hide the big honking navigation section in the header so that they only see it when they want to. In order to make this work, I’m going to write some new CSS, and use some JavaScript to apply the new CSS to the existing HTML.
First, let’s take a look at the new CSS. Step one is to hide the ul
elements in the header so they don’t show up when the user first loads the page. If you are following along at home, open your android.css
file and add the following:
#header ul.hide { display: none; }
This won't actually hide anything until you add the hide
class to the ul
elements (you'll do this shortly with some JavaScript). Next, I’ll define the styles for the button that will show and hide the menu. Note that we haven’t created the HTML for the button yet; For your information, it’s going to look like this:
<div class="leftButton" onclick="toggleMenu()">Menu</div>
I’ll describe the button HTML
in detail a bit further down (the section called “Adding Basic Behavior with jQuery”), so don’t add the preceding line of code to your HTML file. The important thing to understand is that it’s a div
with the class leftButton
and it’s going to be in the header.
Here is the CSS style for the button (you can go ahead and add this to the android.css
file):
#header div.leftButton { position: absolute; top: 7px; left: 6px; height: 30px; font-weight: bold; text-align: center; color: white; text-shadow: rgba(0,0,0,0.6) 0px -1px 1px; line-height: 28px; border-width: 0 8px 0 8px; -webkit-border-image: url(images/button.png) 0 8 0 8; }
Taking it from the top, I set the position to absolute to remove the | |
Here, I set the height to 30px so it’s big enough to tap easily. | |
Next, I style the text bold, white with a slight drop shadow, and centered in the box. | |
In CSS, the rgb function is an alternative to the familiar hex notation typically used to specify colors (e.g. | |
The line-height declaration moves the text down vertically in the box so it’s not flush up against the top border. | |
The With the border-width line, I’m telling the browser to apply a 0 width border to the top, an 8px border to the right, a 0 width border to the bottom, and an 8px width border to the left (i.e. the four parameters start at the top of the box and work their way around clockwise). Note that I don’t need to specify a color or style for the border. | |
With the border widths in place, I can apply the border image. The five parameters from left to right are: the url of the image, the top width, the right width, the bottom width, and the left width (again, clockwise from top). The url can be absolute (http://example.com/myBorderImage.png) or relative. Relative paths are based on the location of the stylesheet, not the HTML page that includes the stylesheet. TipWhen I first encountered the border image property, I found it odd that I had to specify the border widths when I had already done so with the It is possible to do something irrational such as applying the right 4 pixels of an image to a border that is 20px wide. To make this work properly, you have to use the optional parameters of |
Okay, time for some JavaScript. In preparation for the JavaScript you’re about to write, you need to update your HTML document to include jquery.js
and android.js
. Add these lines to the head
section of your HTML document:
<script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="android.js"></script>
jQuery downloads, documentation, and tutorials are available at http://jquery.com. To use jQuery, you will need to download it from there, rename the file you downloaded (such as jquery-1.3.2.min.js
) to jquery.js
, and put a copy of it in the same directory as your HTML document.
The primary duty of the JavaScript in android.js
is to allow the user to show and hide the nav menus. Copy the following JavaScript into a file called android.js
and save it in the same folder as the HTML file:
if (window.innerWidth && window.innerWidth <= 480) { $(document).ready(function(){ $('#header ul').addClass('hide'); $('#header').append('<div class="leftButton" onclick="toggleMenu()">Menu</div>'); }); function toggleMenu() { $('#header ul').toggleClass('hide'); $('#header .leftButton').toggleClass('pressed'); } }
The entire block of code is wrapped in an CautionIf you are testing your Android web pages using the desktop version of Chrome as described in the section called “Don’t have a website?”, the | |
Here we have the so called “document ready” function. If you are new to jQuery, this can be a bit intimidating, and I admit that it took me a while to memorize the syntax. However, it’s worth taking the time to commit it to memory because you’ll be using it a lot. The document ready function basically says, “when the document is ready, run this code.” More on why this is important in a sec. | |
This is typical jQuery code that begins by selecting the CautionHad we not wrapped this line in the document ready function, it would have mostly likely executed before the | |
Here is where we append a button to the header that will allow the user to show and hide the menu (Figure 2.8, “The menu button has been added to the toolbar dynamically using jQuery.”). It has a class that corresponds to the CSS we wrote previously for | |
The | |
Here, I’m toggling the |
Come to think of it, we haven’t written the CSS for the pressed
class yet so let’s do so now. Go back to android.css
and insert the following:
#header div.pressed { -webkit-border-image: url(images/button_clicked.png) 0 8 0 8; }
As you can see, I’m simply specifying a different image for the button border (it happens to be slightly darker). This will add a two-state effect to the button that should make it evident to the user that the button can both show and hide the menu (Figure 2.9, “The menu button displays darker when it has been pressed to display the menu options.”). Figure 2.10, “A tall view of the completed basic Android CSS.” shows a stretched-out view of the page showing both the menu and some of the text.
In this chapter, I covered the basics of converting an existing web page to a more Android friendly format. I even used on a bit of dynamic HTML to show and hide the navigation panels. In the next chapter, I’ll build on these examples while introducing some more advanced JavaScript concepts; in particular, some yummy Ajax goodness.