I have been working sporadically on my math quiz application for the past several weeks. To complicate matters beyond just learning GWT, I decided to optimize the user interface for the iPhone. That way my daughter could practice her math on the go.
First of all, here is the current version of the application, deployed on Google App Engine:
http://1.latest.mookiemath.appspot.com/
Notice a few things:
- After you login, the application shows you the quiz screen directly.
- You can click or tap on the buttons at the bottom of the screen to answer the problems.
- If you have a keyboard, you can also use the corresponding number keys (capturing the browser key press events is for another post)
- After you complete six problems, the application saves the result to the server.
- You can click on the little door icon to switch to a history view that lists your quiz results.
Overall, a very minimalist interface designed for a small screen.
Once I mastered how to apply styles to widgets, there were only two specific tweaks I had to make for the iPhone:
Scaling
Since this is a full-screen application rather than a web page, it needs to disable scaling by adding this meta tag to host page header:
<meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0;">
Scrolling
The history view consists of a fixed header and a scrollable body:
<g:DockLayoutPanel unit="EM"> <g:north size="3.2"> <g:FlowPanel> <g:Label ui:field="iphoneHelp"> Use two fingers to scroll </g:Label> <g:Button ui:field="quizButton" text="Quiz"/> </g:FlowPanel> </g:north> <g:center> <g:ScrollPanel> <g:FlowPanel ui:field="history" /> </g:ScrollPanel> </g:center> </g:DockLayoutPanel>
A ScrollPanel
is basically just a DIV
with the CSS overflow
property set to auto
. This works fine in a full-sized browser, but mobile Safari does not respect the auto
setting by showing a scrollbar when the content overflows. Instead, you have to use a two-finger gesture to scroll the content.
I didn't know this until today, and I expect that many other people do not either, so I wanted to add a simple help label that would only appear for iPhone users. GWT provides "deferred binding" as a general technique for substituting client classes (including widgets) based on user agent or other properties, but that seemed like overkill for this one label. Instead I just relied on a simple native method in my HistoryPanel
widget to retrieve the user agent and hide the help label for non-iPhone users:
public class HistoryPanel extends Composite { ... @UiField Label iphoneHelp; public HistoryPanel(final HandlerManager handlerManager) { DockLayoutPanel parent = uiBinder.createAndBindUi(this); initWidget(parent); if (getUserAgent().indexOf("iphone") == -1) { iphoneHelp.removeFromParent(); } ... } private native String getUserAgent() /*-{ return navigator.userAgent.toLowerCase(); }-*/; ... }
Other than those two issues, the layout looks surprisingly consistent on full-sized browsers and the iPhone.