Introduction
Here I’m going to review the programming technologies used
in the Twentyfour Challenge. These include:- Html5
- CSS3
- Javascript
- JQuery Mobile
- PhoneGap
Html5
There’s not too much to say about Html5 as I only used a
couple of features that are specific to it:
local storage and the <audio>
tag. I also used a specific tag that is crucial to
all PhoneGap projects.
Local storage is incredibly easy to use. I use it to store the game options for
Twentyfour and to keep track of the high scores. The code below saves the game options:
function
storeOptions () {
// must use JSON stringify here!
localStorage["tfc.options"] =
JSON.stringify(options);
}
Here’s the code that reads the stored options:
function loadOptions
() {
// are options in localStorage?
if (localStorage["tfc.options"]) {
options =
JSON.parse(localStorage["tfc.options"]);
// Options that are not strings need to be
converted
options.difficulty =
Number(options.difficulty);
options.count = Number(options.count);
options.soundEnabled = Number(options.soundEnabled);
}
options.count = Number(options.count);
options.soundEnabled = Number(options.soundEnabled);
}
game.numSystem = options.numSystem;
game.difficultyLevel = options.difficulty;
game.countProblems = options.count;
game.soundEnabled = options.soundEnabled;
};
The
tag is complemented by a simple DOM-oriented API to play the sound file that is
referenced. It is also very easy to
use. The sound file (applause) worked in
the Chrome browser, the iOS simulator, and an iPhone 4 device running iOS6. It did not work in my own Android
device (Samsung Galaxy Stellar I200), my iPad2, and another iPhone4 running
iOS7. I know I did everything right so I
decided not to advertise the applause function.
A crucial <meta> tag needed by PhoneGap (really just the html5 compliant browser) is:
CSS3
Javascript
JQuery Mobile
PhoneGap
Summary
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, minimum-scale=1, maximum-scale=1" >
There are many stackoverflow questions regarding the
application’s web page being shown in a teensy-weensy font. The solution is to add the above meta tag.
There’s one more thing that almost drove me crazy regarding
a brower-based application – the back button.
The application runs as a “WebView” (Android terminology) where the
browser does not display any “chrome”
(not Google Chrome, this is chrome like the back button, forward button,
refresh, etc.). This works fine on iOS
but on Android there is a hardware back button.
I found that while playing a game of Twentyfour you must not use the back button at all as it destroys
the state tracking that the app must perform.
I will discuss the solution later in this article.
CSS3
Most of the styling for Twentyfour
comes from JQuery Mobile. My hat is off
to the professionals that developed CSS/html code that is portable across Safari,
Chrome, and Internet Explorer, especially since the vendors of these browsers
compete against one another.
I did, however, have to define a few styles for specific
situations. Here’s the problem “goal”
which is really a button with some style changes.
The style changes are:
.goal-style {
text-align: center;
background: chartreuse;
ui-disabled: true;
}
The trick to
achieving this style is to add it dynamically when the page is rendered.
$('#problem-goal').addClass('goal-style');
The most important reason for learning CSS is due to the
fact that JQuery uses its notion of a selector extensively.
Javascript
Ah, Javascript, how do I hate thee? I speak these words as a Java programmer who
trusts the statically typed language completely. Remember that Twentyfour started as a native Android application that at its heart
contained six classes with 775 lines of code (carriage returns counted here). This includes a simple algebraic expression
parser and evaluator.
Since I was skeptical of translating my Java code to
Javascript by hand, I first sought an automated solution in the Google Web
Toolkit (GWT). Google created the GWT so
they could create sophisticated browser-based applications (think Google Maps)
in Java and then convert the client part to html/javascript. I did get my 775 lines of java code
translated by GWT, but then realized that there was no way to incorporate the
generated javascript into a PhoneGap application.
So I hand translated to Javascript using techniques documented
by David Flanagan in “Javascript: The Definitive Guide.” It resulted in six classes and 588 lines of
code.
Yes, there were type conversion surprises during
development, but thorough testing flushed them out. I developed unit test cases for the important
code and gained the desired degree of confidence in the reincarnated algebraic
expression parser and evaluator.
On every page of the application I included all of
the Javascript files of the application (including JQuery Mobile files). This was recommended in various books and
Internet articles that I read. It also
benefits the use of a Javascript debugger.
Chrome, for example, will not allow you to set breakpoints on a page if
it doesn’t see Javascript included on the page.
There are a couple of takeaways from this:
- You must order your Javascript includes avoiding forward references.
- You have to live with an enormous number of global variables.
JQuery Mobile
The technology I had the most difficulty with was JQuery
Mobile (JQM). The technology (including
JQuery itself) has been going through many rapid revisions. And there is the fundamental fact that JQM
loads a document into the browser’s DOM and manipulates that one DOM
thereafter. This means that you get one
chance to set up your event handlers.
Let me backtrack and try to re-express myself. For the longest time I could not get my JQM
page events to fire properly. My
stackoverflow searches resulted in conflicting information: “use ‘bind’ on the pageinit event.” “No use ‘on’ event.” “Sorry you’re both wrong use
‘delegate’.” No matter what I tried the
JQM event never fired off my handler code.
Then I realized that the DOM is created exactly once by JQM
and just updated when new pages are loaded via AJAX (really HJAX here). The “binding” code that worked for me is
shown below.
$(document).delegate('#index',
'pageinit', function() {
console.log('#index: pageinit');
// local event handlers
$('#play').on('click', function() {
console.log('play clicked');
startGame();
});
});
This breakthrough got me off the dime so I could learn the
JQM widgets and events. The most
confusing event is one of the most important – the ‘click’ event. Due to the nature of clicking using a mouse
versus tapping on a touch sensitive screen, JQM differentiates separate events:
‘tap,’ ‘click,’ and ‘vclick.’ I started
with ‘click’ but found it introduces an annoying 300 ms delay which isn’t good
for rapid play. So I switched to ‘tap’
which has no delay by occasionally has its own unwanted side-effects (ghost
clicks). ‘vclick’ wasn’t any improvement
so I left the implementation with ‘tap.’
I finally learned that 'tap' works fine as long as you immediately
stopped event propagation.
// event
handlers
$("#score-ok-btn").tap
(function(e) {
e.preventDefault();
gamePauseAudio();
$.mobile.changePage('index.html');
});
Another surprise from JQM is that when you manipulate one of
its widgets you must explicitly tell JQM to refresh its display of the widget.
$('#num-probs').val(game.countProblems).slider('refresh');
There were tons of stackoverflow questions asking, “Why
doesn’t my updated widget show the update?”
Answer: “you didn’t refresh it.”
PhoneGap
As I mentioned in a previous blog PhoneGap gives you free
packaging of your browser application and some level of Javascript access to
native device APIs.
I thought I could live without any need for device-specific
API but that Android hardware “back” button was a source of much aggravation. If the user clicked in the middle of a game
the entire game state would be invalid and many Javascript errors would occur.
So the back button became my nemesis as it has been forever
for web developers. I thought of several
approaches:
- Disable the back button
- Manipulate the browser’s stack of “back” pages
- Catch every page transition and only allow the transitions for a “normal” game
- Detect when the hardware back button was pressed
Well #1 is prohibited.
#2 is difficult given the DOM APIs available. #3 requires a lot of careful programming and
is very brittle. #4 is supported by
PhoneGap. After exploring #1 through #3,
I found A PhoneGap API (see the Events API)
that solved the problem completely.
When the hardware back button is pressed on Android (iOS doesn’t have
this) then just jump to the application homepage. Thank you, PhoneGap!
PhoneGap also does the packaging and in this area it
encourages you to use Adobe’s PhoneGap build service. I did not use this approach for a couple of
reasons:
- It costs money (there is a limited free plan)
- You need to package, deploy, and test your app on real devices before a release build.
The build service drives off the application’s config.xml
file. I discovered more documentation on
config.xml at Adobe’s build site than I did at W3 Consortium (there’s a minor
standard for widget packaging) and at the PhoneGap site.
It is not clear to me if the PhoneGap CLI tools process
config.xml the same as the PhoneGap build service.
Summary
There’s a lot of technologies involve here. JQuery Mobile keeps its promise for great
looking portable UIs across mobile clients (Javascript too). And PhoneGap delivers in its packaging for
specific platforms.
Other postings:
Other postings:
- Phonegap Diaries: Intro to 24 Challenge
- Phonegap Diaries: Tools
- Phonegap Diaries: Process
- Phonegap Diaries: Technologies
- Phonegap Diaries: Summary
No comments:
Post a Comment