We can draw text, graphics, and animations in a page of HTML5.
The page can be displayed by both Android and Apple iOS,
so you only have to write it once.
The page is displayed in a
WebView
object in Android,
and in a
UIWebView
object in iOS.
These objects act as tiny web browsers.
Android documentation:
class
WebView,
Building
Web Apps in WebView,
and
Debugging
Web Apps.
iOS documentation: see
this example.
In the page of HTML5,
the graphics are drawn in the language JavaScript using a
canvas
object.
See the
HTML5
canvas
tutorial.
The page of HTML or HTML5 displayed by a
WebView
can come from one of three sources:
.java
file (exercise 5)MainActivity.java:
onCreate
configures the
WebView
object.
activity_main.xml
creates the
WebView.
res/values/dimens/dimens.xml
res/values-w820dp/dimens.xml
for wider screens.
japan.html
in the
assets
folder.
Multiplication by π/180 converts
degrees
to
radians.
AndroidManifest.xml:
a
WebView
requires the
<uses-permission>
element (exercise 4).
build.gradle
(Module: app)
Select the
app
folder at the top of the Android Studio
project
view
and pull down
File →
New →
Folder →
Assets Folder
Creates a source root for assets which will be included in the APK.
☐ Change Folder Location
Target Source Set: main (vs. debug or release)
Finish
Control-click on the
assets
folder you just created in the Android Studio
project
view
and select
New →
File
Enter a new file name: japan.html
OK
activity_main.xml
specified that the
WebView
should occupy the entire
RelativeLayout
(“match
parent, match
parent”),
but it doesn’t.
The
RelativeLayout
is blue,
and part of it is visible because of its padding specified in
activity_main.xml.
Set the horizontal and vertical padding to
0dp
in
dimens.xml.
japan.html,
change
<BODY STYLE = "background-color: cyan;" onLoad = "load();">to
<BODY STYLE = " background-color: cyan; margin-left: 0px; margin-top: 0px; " onLoad = "load();">
alert
prints the dimensions of the canvas in dp:
originaly 328 × 527
and now 360 × 559.
window.innerWidth
and
window.innerHeight
from receiving their correct values until a fraction of a second after
load
is called.
I fixed it by changing
load
to the following.
//This function is called when the web page has been loaded into the WebView. function load() { window.setTimeout(load2, 300); //milliseconds } //This function is called 300 milliseconds after the web page has been loaded //into the WebView. function load2() { try { var canvas = document.getElementById("flag"); //etc.: all the code that was originally in load.
onCreate,
change
webView.loadUrl("file:///android_asset/japan.html");
to
webView.loadUrl("http://www.nyt.com/"); //New York Times
Add the following element to the
AndroidManifest.xml
file immediately before the
<application>
element.
(It’s already added.)
<uses-permission android:name="android.permission.INTERNET" />Then change it back.
onCreate,
comment out the call to
loadUrl
and comment in the call to
loadData.
load
function do something more interesting.
Change the page of HTML to the following.
See the
Manhattan project
and
HTML5 on iPhone.
<!doctype html>
<HTML>
<HEAD>
<META CHARSET = "utf-8">
<TITLE>Manhattan</TITLE>
<SCRIPT TYPE = "text/javascript">
//Constructor for class Location.
function Location(longitude, latitude) {
this.longitude = longitude;
this.latitude = latitude;
}
//This function is called when the web page has been loaded into the WebView.
function load() {
window.setTimeout(load2, 300); //milliseconds
}
//This function is called 300 milliseconds after the web page has been loaded
//into the WebView.
function load2() {
try {
var canvas = document.getElementById("map");
var context = canvas.getContext("2d");
//Make the canvas occupy the whole web page.
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//Fill the map with a white background.
context.fillStyle = "white";
context.fillRect(0, 0, canvas.width, canvas.height);
/*
An array of Location objects,
counterclockwise around the shore of Manhattan.
Longitude west of Greenwich is negative.
Latitude north of the equator is positive.
*/
var point = new Array(
new Location(-73.971548, 40.72921), //East River at East 17th Street
new Location(-73.974595, 40.735519), //24
new Location(-73.971806, 40.742998), //34
new Location(-73.96215, 40.754767), //53
new Location(-73.954296, 40.762146), //65
new Location(-73.946185, 40.771474), //81
new Location(-73.942022, 40.776154), //89
new Location(-73.942022, 40.776154), //96
new Location(-73.93816, 40.787008), //103
new Location(-73.929534, 40.795326), //118
new Location(-73.929062, 40.800946), //125
new Location(-73.934212, 40.808775), //Harlem River at 132nd Street
new Location(-73.933868, 40.817772), //143
new Location(-73.935113, 40.83547), //163
new Location(-73.922195, 40.855857), //Dyckman Street
new Location(-73.91078, 40.869878), //218
new Location(-73.911767, 40.873416), //Broadway Bridge
new Location(-73.922968, 40.877018), //Henry Hudson Parkway Bridge
new Location(-73.926916, 40.877082), //Hudson River
new Location(-73.933096, 40.867379), //Riverside Drive
new Location(-73.943224, 40.852417), //Hudson River at West 181st Street
new Location(-73.946786, 40.850339), //George Washington Bridge
new Location(-73.946786, 40.850339), //168
new Location(-73.95052, 40.834626), //155
new Location(-73.955026, 40.827417), //144 sewage treatment plant
new Location(-73.956399, 40.828034), //144
new Location(-73.959446, 40.82365), //137
new Location(-73.957601, 40.822676), //137
new Location(-73.994765, 40.771669), //57
new Location(-73.995152, 40.769524), //54
new Location(-73.999872, 40.763316), //44
new Location(-74.001718, 40.762276), //42
new Location(-74.007726, 40.754052), //29
new Location(-74.009442, 40.749825), //23
new Location(-74.00794, 40.748362), //21
new Location(-74.009228, 40.740754), //Meatpacking District
new Location(-74.010344, 40.739258), //Gansevoort Street
new Location(-74.011545, 40.726218), //Holland Tunnel
new Location(-74.013176, 40.718315), //Battery Park City
new Location(-74.016609, 40.718737), //Battery Park City
new Location(-74.019227, 40.706539), //South Cove
new Location(-74.014893, 40.70078), //Battery Park
new Location(-74.009314, 40.701919), //Heliport
new Location(-73.997984, 40.708523), //north of Brooklyn Bridge
new Location(-73.977985, 40.710475), //Corlears Hook Park
new Location(-73.976011, 40.712752), //Grand Street
new Location(-73.972964, 40.720819) //East 6th Street
);
//Three transformations:
//Transformation 1: move the origin (0, 0) to the center of the canvas.
context.translate(canvas.width / 2, canvas.height / 2);
//Center of Manhattan, near the Angel of the Waters in Central Park.
var center = new Location(-73.965, 40.79);
var latitude = center.latitude * Math.PI / 180; //latitude in radians
//The screen will cover 1/4 of a degree of latitude vertically,
//approx 15 miles. New York, New York, it's a helluva town!
//pixels per degree of latitude (approx 60 miles)
var verticalScale = 4 * canvas.height;
//pixels per degree of longitude (approx 45 miles in New York)
var horizontalScale = verticalScale * Math.cos(latitude);
//Transformation 2: magnify the map, and make the Y axis point up.
context.scale(horizontalScale, -verticalScale);
//Transformation 3: move the camera from Latitude 0, Longitude 0
//(in the South Atlantic) to the center of Manhattan.
context.translate(-center.longitude, -center.latitude);
context.beginPath();
context.moveTo(point[0].longitude, point[0].latitude);
for (var i = 1; i < point.length; ++i) {
context.lineTo(point[i].longitude, point[i].latitude);
}
context.closePath();
context.fillStyle = "red";
context.fill();
} catch (exception) {
alert("canvas not supported: " + exception);
}
}
</SCRIPT>
</HEAD>
<BODY STYLE = "
background-color: cyan;
margin-left: 0px;
margin-top: 0px;
" onLoad = "load();">
<CANVAS ID = "map">
</CANVAS>
</BODY>
</HTML>
Activity
object call a Javascript function in the web page.
We can’t do this until the page is fully loaded.
A
WebViewClient
will tell us when the page is fully loaded.
Create another Javascript function in the web page.
function f() { alert(window.innerWidth + " \u00D7 " + window.innerHeight); }In
onCreate,
insert a
WebViewClient
into the
WebView
before inserting the
WebChromeClient
into the
WebView.
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
Toast toast = Toast.makeText(MainActivity.this, url + " loaded", Toast.LENGTH_LONG);
toast.show();
view.loadUrl("javascript:f()");
}
});
The toast says “file:///android_asset/japan.html loaded”.
How could we return a return value from
f?