My current solution for displaying dynamic interactive maps in my Android applications is to display a HTML-based Google Map within an instance of StageWebView. For the most part this has worked well, except for the long start up process. This often gives the appearance of the application being frozen. So, I sought a solution to try to improve the user experience.
The first step was to find a way to hide the StageWebView until it had finished its initialization and contacted the server. Since this component is of the ‘Stage’ type, this means that the normal display manipulation will not work. Instead, I created the viewport of the StageWebView offscreen. Now, I just need to know when it has contacted the server and the first page has been loaded. For this, you can listen for the Location Changed event. (Note, there appears to be a bug in AIR for Android 3.0, where this event is only fired upon the first URL that is accessed. This is important later).
Once the page has been loaded into our StageWebView, I reset the viewport to be on-screen. Since I have a loading spinner animating on the stage, this delay is now neatly masked.
However, my screen still shows a white StageWebView. Then the map tiles are displayed, followed by the map controls. So how can I improve this portion of the process?
In exploring the Google Map forums, I came across the notion of first serving a static Google Map, then waiting for the “tiles_loaded” event to be fired to swap the static map with the dynamic one. This was as simple as adding an <img> tag for the static map, and an event listener. In my web browser, this solution worked flawlessly.
Since there is a Location_Changing event that StageWebView is supposed to fire when the URL is changing, I thought I could tie into this by changing the page’s URL with an # attribute. But I then learned that this event does not currently properly fire on Android devices. I had planned to keep the StageWebView off-screen until the dynamic version of the map was ready before changing its viewport location.
Instead, I was forced to display the static map, then allow the loading of tiles and the map controls to occur visibly to the user. I soon ran into another problem. I had defined the width of the static map to be 480 pixels wide (I would have custom versions of the html page per device width), but once the html file was loaded into the StageWebView, I was able to scroll the image horizontally. What was going on!? Everything was working fine in my desktop browsers.
So, I began looking into the various attributes of the viewport meta tag, to see if there was something I could do to adjust the viewport. If you are not familiar with this tag, the Android documentation has a great reference on it. If you read through their documentation, you will learn that web pages on high-density devices (which is required to run AIR for Android) are automatically scaled by 150%. Eureka! Now I had confirmed why the static image became scrollable, now to find the solution. Reading further, there is another interesting attribute that can be set: target-densitydpi. To quote the documentation, “You can change the target screen density for your web page using the target-densitydpi viewport property.” This property has five values; device-dpi, high-dpi, medium-dpi, low-dpi, or a value. The one of interest for us is the device-dpi. If the target-densitydpi is set to device-dpi, the page will use the device’s native dpi as the target dpi and default scaling never occurs. And with this one addition to our viewport tag, our html file will now renders correctly on-device. Here is a sample apk and the source files.