使用HTML5开发android
一、Android设备多分辨率的问题
1. 显示差异
Android浏览器默认预览模式浏览会缩小页面,而在WebView中则会以原始大小显示。同时,Android浏览器和WebView默认为mdpi,hdpi相当于mdpi的1.5倍,ldpi相当于0.75倍。
2. 解决方式
(1)viewport属性
viewport属性需放在HTML的<meta>标签中。以下是示例代码:
<head>
<title>Exmaple</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
</head>
<meta>中viewport的完整属性如下:
<meta name="viewport"
content="height=[pixel_value | device-height],
width=[pixel_value | device-width],
initial-scale=float_value,
minimum-scale=float_value,
maximum-scale=float_value,
user-scalable=[yes | no],
target-densitydpi=[dpi_value | device-dpi | high-dpi | medium-dpi | low-dpi]" />
(2)CSS控制
- 创建独立样式表:为每种密度创建独立的样式表,其中
webkit-device-pixel-ratio的3个数值对应3种分辨率。示例代码如下:<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.5)" href="hdpi.css" /> <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.0)" href="mdpi.css" /> <link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 0.75)" href="ldpi.css" /> - 在一个样式表中指定不同样式:
#header { background:url(medium-density-image.png); } @media screen and (-webkit-device-pixel-ratio: 1.5) { // CSS for high-density screens #header { background:url(high-density-image.png); } } @media screen and (-webkit-device-pixel-ratio: 0.75) { // CSS for low-density screens #header { background:url(low-density-image.png); } }同时,可添加如下
<meta>标签:<meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
(3)JS控制
Android浏览器和WebView支持查询当前设备密度的DOM特性window.devicePixelRatio,其值有3个(0.75、1、1.5对应3种分辨率)。以下是JS中查询设备密度的方法:
if (window.devicePixelRatio == 1.5) {
alert("This is a high-density screen");
} else if (window.devicePixelRatio == 0.75) {
alert("This is a low-density screen");
}
二、Android中构建HTML5应用
1. 使用WebView控件
使用WebView控件与其他控件的使用方法相同,在layout中使用一个<WebView>标签。WebView不包括导航栏、地址栏等完整浏览器功能,仅用于显示一个网页。在WebView中加载Web页面,使用loadUrl()方法,示例代码如下:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
注意,需在manifest文件中加入访问互联网的权限:
<uses-permission android:name="android.permission.INTERNET" />
2. 链接处理
在Android中点击一个链接,默认是调用应用程序来启动,因此WebView需要代为处理这个动作,可通过WebViewClient实现:
//设置WebViewClient
webView.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
});
WebViewClient对象可以自己扩展,例如:
private class MyWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (Uri.parse(url).getHost().equals("www.example.com")) {
return false;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
}
之后使用自定义的WebViewClient:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new MyWebViewClient());
3. 历史记录处理
出于用户习惯考虑,需要将WebView表现得更像一个浏览器,即可以回退历史记录。因此需要覆盖系统的回退键,goBack、goForward可向前向后浏览历史页面,示例代码如下:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
同时,可开启JavaScript支持:
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
这里的webSettings用处非常大,可以开启很多设置,在之后的本地存储、地理位置等功能中都会使用到。
4. 交互调用
(1)在JS中调用Android的函数方法
首先,在Android程序中建立接口:
final class InJavaScript {
public void runOnAndroidJavaScript(final String str) {
handler.post(new Runnable() {
public void run() {
TextView show = (TextView) findViewById(R.id.textview);
show.setText(str);
}
});
}
}
然后,将该接口实例添加到js的全局对象window中:
webView.addJavascriptInterface(new InJavaScript(), "injs");
在JavaScript中调用:
function sendToAndroid(){
var str = "Cookie call the Android method from js";
window.injs.runOnAndroidJavaScript(str);
}
(2)在Android中调用JS的方法
在JS中的方法如下:
function getFromAndroid(str){
document.getElementById("android").innerHTML=str;
}
在Android中调用该方法:
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
//调用javascript中的方法
webView.loadUrl("javascript:getFromAndroid('Cookie call the js function from Android')");
}
});
(3)在Android中处理JS的警告、对话框等
需要对WebView设置WebChromeClient对象:
webView.setWebChromeClient(new WebChromeClient(){
//处理javascript中的alert
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//构建一个Builder来显示网页中的对话框
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Alert");
builder.setMessage(message);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setCancelable(false);
builder.create();
builder.show();
return true;
};
//处理javascript中的confirm
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("confirm");
builder.setMessage(message);
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
builder.setCancelable(false);
builder.create();
builder.show();
return true;
};
@Override
//设置网页加载的进度条
public void onProgressChanged(WebView view, int newProgress) {
MainActivity.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress * 100);
super.onProgressChanged(view, newProgress);
}
//设置应用程序的标题title
public void onReceivedTitle(WebView view, String title) {
MainActivity.this.setTitle(title);
super.onReceivedTitle(view, title);
}
});
三、Android中的调试
1. 通过JS代码输出log信息
console.log("Hello World");
Log信息示例:Console: Hello World http://www.example.com/hello.html :82
2. 在WebChromeClient中实现回调方法
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebChromeClient(new WebChromeClient() {
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
Log.d("MyApplication", message + " -- From line "
+ lineNumber + " of "
+ sourceID);
}
});
或者使用另一种重载方法:
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebChromeClient(new WebChromeClient() {
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.d("MyApplication", cm.message() + " -- From line "
+ cm.lineNumber() + " of "
+ cm.sourceId() );
return true;
}
});
ConsoleMessage还包括一个MessageLevel表示控制台传递信息类型。您可以用messageLevel()查询信息级别,以确定信息的严重程度,然后使用适当的Log方法或采取其他适当的措施。
四、HTML5本地存储在Android中的应用
1. HTML5存储方法
HTML5提供了2种客户端存储数据新方法:
localStorage:没有时间限制。sessionStorage:针对一个Session的数据存储。
示例代码如下:
<script type="text/javascript">
localStorage.lastname = "Smith";
document.write(localStorage.lastname);
</script>
<script type="text/javascript">
sessionStorage.lastname = "Smith";
document.write(sessionStorage.lastname);
</script>
2. WebStorage的API
//清空storage
localStorage.clear();
//设置一个键值
localStorage.setItem("yarin", "yangfegnsheng");
//获取一个键值
localStorage.getItem("yarin");
//获取指定下标的键的名称(如同Array)
localStorage.key(0);
//删除一个键值
localStorage.removeItem("yarin");
注意,一定要在设置中开启localStorage支持:
webSettings.setDomStorageEnabled(true);
3. 在Android中进行操作
//启用数据库
webSettings.setDatabaseEnabled(true);
String dir = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
//设置数据库路径
webSettings.setDatabasePath(dir);
//使用localStorage则必须打开
webSettings.setDomStorageEnabled(true);
//扩充数据库的容量(在WebChromeClinet中实现)
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota,
long estimatedSize, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(estimatedSize * 2);
}
4. 在JS中进行数据库操作
function initDatabase() {
try {
if (!window.openDatabase) {
alert('Databases are not supported by your browser');
} else {
var shortName = 'YARINDB';
var version = '1.0';
var displayName = 'yarin db';
var maxSize = 100000; // in bytes
YARINDB = openDatabase(shortName, version, displayName, maxSize);
createTables();
selectAll();
}
} catch (e) {
if (e == 2) {
// Version mismatch.
console.log("Invalid database version.");
} else {
console.log("Unknown error " + e + ".");
}
return;
}
}
function createTables(){
YARINDB.transaction(
function (transaction) {
transaction.executeSql('CREATE TABLE IF NOT EXISTS yarin(id INTEGER NOT NULL PRIMARY KEY, name TEXT NOT NULL,desc TEXT NOT NULL);', [], nullDataHandler, errorHandler);
}
);
insertData();
}
function insertData(){
YARINDB.transaction(
function (transaction) {
//Starter data when page is initialized
var data = ['1', 'yarin yang', 'I am yarin'];
transaction.executeSql("INSERT INTO yarin(id, name, desc) VALUES (?, ?, ?)", [data[0], data[1], data[2]]);
}
);
}
function errorHandler(transaction, error){
if (error.code == 1) {
// DB Table already exists
} else {
// Error is a human-readable string.
console.log('Oops. Error was ' + error.message + ' (Code ' + error.code + ')');
}
return false;
}
function nullDataHandler(){
console.log("SQL Query Succeeded");
}
function selectAll(){
YARINDB.transaction(
function (transaction) {
transaction.executeSql("SELECT * FROM yarin;", [], dataSelectHandler, errorHandler);
}
);
}
function dataSelectHandler(transaction, results){
// Handle the results
for (var i = 0; i < results.rows.length; i++) {
var row = results.rows.item(i);
var newFeature = new Object();
newFeature.name = row['name'];
newFeature.decs = row['desc'];
document.getElementById("name").innerHTML = "name:" + newFeature.name;
document.getElementById("desc").innerHTML = "desc:" + newFeature.decs;
}
}
function updateData(){
YARINDB.transaction(
function (transaction) {
var data = ['fengsheng yang', 'I am fengsheng'];
transaction.executeSql("UPDATE yarin SET name=?, desc=? WHERE id = 1", [data[0], data[1]]);
}
);
selectAll();
}
function ddeleteTables(){
YARINDB.transaction(
function (transaction) {
transaction.executeSql("DROP TABLE yarin;", [], nullDataHandler, errorHandler);
}
);
console.log("Table 'page_settings' has been dropped.");
}
function initLocalStorage(){
if (window.localStorage) {
textarea.addEventListener("keyup", function () {
window.localStorage["value"] = this.value;
window.localStorage["time"] = new Date().getTime();
}, false);
} else {
alert("LocalStorage are not supported in this browser.");
}
}
window.onload = function () {
initDatabase();
initLocalStorage();
}
五、HTML5地理位置服务在Android中的应用
1. Android端设置
//启用地理定位
webSettings.setGeolocationEnabled(true);
//设置定位的数据库路径
webSettings.setGeolocationDatabasePath(dir);
//配置权限(同样在WebChromeClient中实现)
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
同时,需在Manifest中添加权限:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2. HTML5端获取地理位置信息
常用的navigator.geolocation对象有以下三种方法:
//获取当前地理位置
navigator.geolocation.getCurrentPosition(success_callback_function, error_callback_function, position_options)
//持续获取地理位置
navigator.geolocation.watchPosition(success_callback_function, error_callback_function, position_options)
//清除持续获取地理位置事件
navigator.geolocation.clearWatch(watch_position_id)
以下是具体的JS代码示例:
//定位
function get_location() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(show_map, handle_error, { enableHighAccuracy: false, maximumAge: 1000, timeout: 15000 });
} else {
alert("Your browser does not support HTML5 geoLocation");
}
}
function show_map(position) {
var latitude = position.coords.latitude;
var longitude = position.coords.longitude;
var city = position.coords.city;
//telnet localhost 5554
//geo fix -82.411629 28.054553
//geo fix -121.45356 46.51119 4392
//geo nmea $GPGGA,001431.092,0118.2653,N,10351.1359,E,0,00,,-19.6,M,4.1,M,,0000*5B
document.getElementById("Latitude").innerHTML = "latitude:" + latitude;
document.getElementById("Longitude").innerHTML = "longitude:" + longitude;
document.getElementById("City").innerHTML = "city:" + city;
}
function handle_error(err) {
switch (err.code) {
case 1:
alert("permission denied");
break;
case 2:
alert("the network is down or the position satellites can't be contacted");
break;
case 3:
alert("time out");
break;
default:
alert("unknown error");
}
}