WebView(网络视图)能加载显示网页,可以将其视为一个浏览器。它使用了WebKit渲染引擎加载显示网页,实现WebView有以下两种不同的方法:
第一种方法的步骤:
1.在要Activity中实例化WebView组件:WebView webView = new WebView(this);
2.调用WebView的loadUrl()方法,设置WevView要显示的网页:
互联网用:webView.loadUrl("http://www.google.com");
本地文件用:webView.loadUrl("file:///android_asset/XX.html"); 本地文件存放在:assets 文件中
3.调用Activity的setContentView( )方法来显示网页视图
4.用WebView点链接看了很多页以后为了让WebView支持回退功能,需要覆盖覆盖Activity类的onKeyDown()方法,如果不做任何处理,点击系统回退剪键,整个浏览器会调用finish()而结束自身,而不是回退到上一页面
5.需要在AndroidManifest.xml文件中添加权限,否则会出现Web page not available错误。
下面是具体例子:
MainActivity.java
package com.android.webview.activity;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebView;
public class MainActivity extends Activity {
private WebView webview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//实例化WebView对象
webview = new WebView(this);
//设置WebView属性,能够执行Javascript脚本
webview.getSettings().setJavaScriptEnabled(true);
//加载需要显示的网页
webview.loadUrl("http://www.51cto.com/");
//设置Web视图
setContentView(webview);
}
@Override
//设置回退
//覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
webview.goBack(); //goBack()表示返回WebView的上一页面
return true;
}
return false;
}
在AndroidManifest.xml文件中的17行添加权限
android:versionCode="1"
android:versionName="1.0">
效果图:
第二种方法的步骤:
1、在布局文件中声明WebView
2、在Activity中实例化WebView
3、调用WebView的loadUrl( )方法,设置WevView要显示的网页
4、为了让WebView能够响应超链接功能,调用setWebViewClient( )方法,设置 WebView视图
5、用WebView点链接看了很多页以后为了让WebView支持回退功能,需要覆盖覆盖Activity类的onKeyDown()方法,如果不做任何处理,点击系统回退剪键,整个浏览器会调用finish()而结束自身,而不是回退到上一页面
6、需要在AndroidManifest.xml文件中添加权限,否则出现Web page not available错误。
下面是具体的例子:
MainActivity.java
package com.android.webview.activity;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends Activity {
private WebView webview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webview = (WebView) findViewById(R.id.webview);
//设置WebView属性,能够执行Javascript脚本
webview.getSettings().setJavaScriptEnabled(true);
//加载需要显示的网页
webview.loadUrl("http://www.51cto.com/");
//设置Web视图
webview.setWebViewClient(new HelloWebViewClient ());
}
@Override
//设置回退
//覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
webview.goBack(); //goBack()表示返回WebView的上一页面
return true;
}
return false;
}
//Web视图
private class HelloWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
}
}
main.xml
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
在AndroidManifest.xml文件中的17行添加权限
android:versionCode="1"
android:versionName="1.0">
在Android手机中内置了一款高性能webkit内核浏览器,在SDK中封装为一个叫做WebView组件
WebView在开发过程中应该注意几点:
1.AndroidManifest.xml中必须使用许可"android.permission.INTERNET",否则会出Webpage not available错误。
2.如果访问的页面中有Javascript,则webview必须设置支持Javascript。
WebSettings webSetting = webview.getSettings();
webSetting.setJavaScriptEnabled(true);
3.如果页面中链接,如果希望点击链接继续在当前browser中响应,而不是新开Android的系统browser中响应该链接,必须覆盖webview的WebViewClient对象。
1 mWebView.setWebViewClient(new WebViewClient(){
2 publicboolean shouldOverrideUrlLoading(WebView view, String url) {
3 view.loadUrl(url);
4 returntrue;
5 }
6 });
4.如果不做任何处理,浏览网页,点击系统“Back”键,整个Browser会调用finish()而结束自身,如果希望浏览的网页回退而不是推出浏览器,需要在当前Activity中处理并消费掉该Back事件。
7 publicboolean onKeyDown(int keyCode, KeyEventevent) {
8 if((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
9 mWebView.goBack();
10 returntrue;
11 }
12 returnsuper.onKeyDown(keyCode,event);
13 }
5.如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点。
webview.requestFocusFromTouch();
6.WebView 加载界面主要调用三个方法:LoadUrl、LoadData、LoadDataWithBaseURL.
1、LoadUrl 直接加载网页、图片并显示.(本地或是网络上的网页、图片、gif)
2、LoadData 显示文字与图片内容(模拟器1.5、1.6)
3、LoadDataWithBase 显示文字与图片内容(支持多个模拟器版本)
WebSettings的常用方法介绍
14 setJavaScriptEnabled(true);
//支持js脚步
15 setPluginsEnabled(true);
//支持插件
16 setUseWideViewPort(false);
//将图片调整到适合webview的大小
17 setSupportZoom(true);
//支持缩放
18 setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);
//支持内容从新布局
19 supportMultipleWindows();
//多窗口
20 setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//关闭webview中缓存
21 setAllowFileAccess(true);
//设置可以访问文件
22 setNeedInitialFocus(true);
//当webview调用requestFocus时为webview设置节点
webSettings.setBuiltInZoomControls(true);
//设置支持缩放
23 setJavaScriptCanOpenWindowsAutomatically(true);
//支持通过JS打开新窗口
24 setLoadsImagesAutomatically(true);
//支持自动加载图片
WebViewClient的方法全解
25 doUpdateVisitedHistory(WebViewview,Stringurl, boolean isReload)
(更新历史记录)
26 onFormResubmission(WebViewview,MessagedontResend,Messageresend)
(应用程序重新请求网页数据)
27 onLoadResource(WebViewview,Stringurl)
在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
28 onPageStarted(WebViewview,Stringurl,Bitmapfavicon)
这个事件就是开始载入页面调用的,通常我们可以在这设定一个loading的页面,告
诉用户程序在等待网络响应。
29 onPageFinished(WebViewview,Stringurl)
在页面加载结束时调用。同样道理,我们知道一个页面载入完成,于是我们可以关
闭loading 条,切换程序动作。
30 onReceivedError(WebViewview, int errorCode, Stringdescription,StringfailingUrl)
(报告错误信息)
31 onReceivedHttpAuthRequest(WebViewview,HttpAuthHandlerhandler,Stringhost,
Stringrealm)(获取返回信息授权请求)
32 onReceivedSslError(WebViewview,SslErrorHandlerhandler,SslErrorerror)
重写此方法可以让webview处理https请求。
33 onScaleChanged(WebViewview, float oldScale, float newScale)
(WebView发生改变时调用)
34 onUnhandledKeyEvent(WebViewview,KeyEventevent)
(Key事件未被加载时调用)
35 shouldOverrideKeyEvent(WebViewview,KeyEventevent)
重写此方法才能够处理在浏览器中的按键事件。
36 shouldOverrideUrlLoading(WebViewview,Stringurl)
在点击请求的是链接是才会调用,重写此方法返回true表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边。这个函数我们可以做很多操作,比如我们读取到某些特殊的URL,于是就可以不打开地址,取消这个操作,进行预先定义的其他操作,这对一个程序是非常必要的。
原来是因为WebView默认没有开启文件下载的功能,如果要实现文件下载的功能,需要设置WebView的DownloadListener,通过实现自己的DownloadListener来实现文件的下载。具体操作如下:
1、设置WebView的DownloadListener:
webView.setDownloadListener(new MyWebViewDownLoadListener());
2、实现MyWebViewDownLoadListener这个类,具体可以如下这样:
这只是调用系统中已经内置的浏览器进行下载,还没有WebView本身进行的文件下载,不过,这也基本上满足我们的应用场景了。
1、添加上网权限:
2、设置webview
1 WebView webView; 2 WebSettings ws; 3 4 5 ws = webView.getSettings(); 6 ws.setAppCacheEnabled(true);// 设置启动缓存 7 ws.setAppCacheMaxSize(1024 * 10);// 设置最大缓存 8 ws.setSupportZoom(true);// 设置成拖动放大缩小 9 ws.setBuiltInZoomControls(true); 10 ws.setCacheMode(WebSettings.LOAD_NO_CACHE);// 设置缓存模式 11 // 设置支持Javascript 12 webView.getSettings().setJavaScriptEnabled(true); 13 webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true); 14 15 // 缓存,离线应用 16 webView.getSettings().setAppCacheEnabled(true); 17 webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); 18 webView.getSettings().setAppCacheMaxSize(1024 * 1024 * 8);
3、获取网页对话框
webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { // TODO Auto-generated method stub // 构建一个Builder来显示网页中的对话框 Builder builder = new Builder(context); builder.setTitle("警告"); builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { // 点击确定按钮之后,继续执行网页中的操作 result.confirm(); } }); builder.setCancelable(false); builder.create(); builder.show(); return true; } @Override public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { // TODO Auto-generated method stub Builder builder = new Builder(context); builder.setTitle("confirm"); builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, new AlertDialog.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) {// 加载进度 // TODO Auto-generated method stub super.onProgressChanged(view, newProgress); } });
4、如果希望点击链接由自己处理,而不是新开Android的系统browser中响应该链接。
给WebView添加一个事件监听对象(WebViewClient)
5、webview取得焦点
webView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub webView.requestFocus(); return false; } });
6、提示加载对话框
pd=ProgressDialog.show(context, "请稍后", "正在加载中...");
当网页加载完成后,调用pd.dismiss();
Settings是WebView提供给上层App的一个配置Webview的接口,每个WebView都有一个WebSettings,要控制WebView的行为,只能通过WebView.getSettings()获取WebSettings对象的引用,然后再改变它的属性。
Browser有一个BrowserSettings用来管理所有的配置信息,它是一个单键(Singleton)。BrowserSettings是直接管理WebSettings的,而UI部分则是由BrowserPreferencePage和Fragment来完成。BrowserSettings实现了OnSharedPreferenceChangedListener,当某个配置项发生变化时,BrowserSettings的接口onPreferencesChanged就会被回调到,然后BrowserSettings就会把配置通过WebSettings应用到WebView中,所以这些Settings都是及时生效的。
一共有三类Settings:共享Settings,静态Settings和动态Settings。静态就是不会在Browser运行时发生变化的,也就是说在Browser的Settings中无法改变的一些配置,这些跟应用程序内部关联较多,比如应用程序的数据存放地点等,而与用户层关系不多;共享Settings是独立于WebView的,也就是说它被系统内所有WebView共享的一些配置,比如Cookie,它不需要为每个WebView单独配置。动态Settings就是用于动态配置WebView的,比如字体,缩放,是否加载图片,是否启用JavaScript等等。
初始化
因为Browser可以有多个窗口,每一个窗口都会有一个WebView,也就是说Browser会管理多个WebView,所以BrowserSettings也要管理多个WebSettings,因为每一个配置的改变都要应用到所有的WebView中。为了能让BrowserSettings能够管理多个WebSettings,它必须持有对这些WebSettings的引用。Browser在创建WebView的时候会把WebView的配置WebSettings通过接口放到BrowserSettings中,BrowserSettings中有一个列表,用于持有对WebSettings的引用。具体的BrowserWebViewFactory中的initWebViewSettings()方法会在每次创建WebView后调用,它把WebView的WebSettings取出,做些必要的初始化,然后放到BrowserSettings中,BrowserSettings.startManagingSettings()会加载SharedPreference文件中的信息对此WebSettings进行初始化,然后放到mManagedSettings列表中。
配置变更
用户进入Settings界面,进行配置的更改,每当有配置发生变化BrowserSettings.onSharedPreferencesChanged()会被回调到,它就会做syncManagedSettings()的动作,把共享设置同步到底层去(也即把设置同步到Cookiemanager中),再遍历列表mManagedSettings,把mPref中的信息全部同步到每个WebSettings中,这样所有的WebView都会实时的响应配置变更。
设置的界面和BrowserSettings没有直接的关联,界面是通过PreferenceActivity和Fragment把各种Settings呈现给用户,而BrowserSettings是把配置信息同步给WebView也就是让配置生效。它们之间的信息桥梁是SharedPreference,也就是说PreferenceActivity和Fragment从用户处接收配置信息,然后放到SharedPreference中,BrowserSettin
在项目中经常会使用到WebView控件,当加载html页面时,会在/data/data/应用package目录下生成database与cache两个文件夹如下图如示:
请求的url记录是保存在webviewCache.db,而url的内容是保存在webviewCache文件夹下.
为了便于理解,接下来模拟一个案例,定义一个html文件,在里面显示一张图片,用WebView加载出来,然后再试着从缓存里把这张图片读取出来并显示。
第一步:新建一个Android工程命名为WebViewCache.目录结构如下:
第二步:在assets目录下新建一个html文件,命名为index.html
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>WebViewCacheDemotitle>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
head>
<body>
<img src="http://img04.taobaocdn.com/imgextra/i4/608825099/T2nGXBXXpaXXXXXXXX_!!608825099.jpg_310x310.jpg"/>
body>
html>
第三步:修改main.xml布局文件,一个WebView控件一个Button(点击加载缓存图片用),代码如下:
xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <WebView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/webView"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="从缓存读取图片" android:id="@+id/button"/> LinearLayout>
第四步:修改主核心程序WebViewCacheDemo.java,这里我只加载了index.html文件,按钮事件暂时没写,代码如下:
package com.ljq.activity; import java.io.File; import java.io.FileInputStream; import android.app.Activity; import android.app.Dialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.ImageView; public class WebViewActivity extends Activity { private WebView webView; private static final String url="file:///android_asset/index.html"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); webView=(WebView)findViewById(R.id.webView); webView.loadUrl(url); } }
第五步:在AndroidMainifest.xml文件中加访问网络的权限:
运行效果如下:
此时我们在WebViewCache.db里的cache.table里多了一条记录如下图所示:
在cache/webviewCache/目录下多了一个10d8d5cd文件,刚好和cache.table里的filepath,我们可以断定这个文件就是我们从网上拽下来的图片:
为了验证猜想,我给Button增加事件响应,就是弹出Dialog,里面加载缓存的图片,完整代码如下:
package com.ljq.activity; import java.io.File; import java.io.FileInputStream; import android.app.Activity; import android.app.Dialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.widget.Button; import android.widget.ImageView; public class WebViewActivity extends Activity { private WebView webView; private static final String url="file:///android_asset/index.html"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); webView=(WebView)findViewById(R.id.webView); webView.loadUrl(url); //点击按钮时弹出对话框 Button button=(Button)findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { ImageView imageView=new ImageView(WebViewActivity.this); imageView.setImageBitmap(getPictureFromCache()); Builder builder=new android.app.AlertDialog.Builder(WebViewActivity.this); //设置对话框的图标 builder.setTitle("从缓存查看图片"); builder.setView(imageView); //退出按钮 builder.setPositiveButton("退 出", new OnClickListener(){ public void onClick(DialogInterface dialog, int which) { //关闭alert对话框架 dialog.cancel(); } }); builder.create().show(); } }); } /** * 从缓存获取图片 * * @return */ private Bitmap getPictureFromCache(){ Bitmap bitmap=null; try { //这里写死,在实际开发项目中要灵活使用 File file=new File(getCacheDir()+"/webviewCache/10d8d5cd"); FileInputStream inStream=new FileInputStream(file); bitmap=BitmapFactory.decodeStream(inStream); } catch (Exception e) { e.printStackTrace(); } return bitmap; } }
第六步:再次运行工程,点击button按钮,效果如下图所示:
01 |
// clear the cache before time numDays |
02 |
private int clearCacheFolder(File dir, long numDays) { |
03 |
int deletedFiles = 0 ; |
04 |
if (dir!= null && dir.isDirectory()) { |
05 |
try { |
06 |
for (File child:dir.listFiles()) { |
07 |
if (child.isDirectory()) { |
08 |
deletedFiles += clearCacheFolder(child, numDays); |
09 |
} |
10 |
if (child.lastModified() < numDays) { |
11 |
if (child.delete()) { |
12 |
deletedFiles++; |
13 |
} |
14 |
} |
15 |
} |
16 |
} catch (Exception e) { |
17 |
e.printStackTrace(); |
18 |
} |
19 |
} |
20 |
return deletedFiles; |
21 |
} |
1 |
//优先使用缓存: |
2 |
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); |
3 |
4 |
//不使用缓存: |
5 |
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); |
01 |
File file = CacheManager.getCacheFileBaseDir(); |
02 |
if (file != null && file.exists() && file.isDirectory()) { |
03 |
for (File item : file.listFiles()) { |
04 |
item.delete(); |
05 |
} |
06 |
file.delete(); |
07 |
} |
08 |
|
09 |
context.deleteDatabase( "webview.db" ); |
10 |
context.deleteDatabase( "webviewCache.db" ); |
webview 是基于 webkit 内核,不过他的运行效果和 firefox 上一模一样,所以写的时候都是先用 firefox 测试,测试 OK 了再放到程序里面看效果,基本上不会有什么问题。其实 android 的 webview 跟 iphone 的 webview 差不多, iphone 上的 webview 比 android 上的强大多了。
谈一下研究 webview 的一些成果:
一. 加载资源的速度不慢,但是资源多了,就很慢。图片、 css 、 js 、 html 这些资源每个大概需要 10-200ms,一般都是 30ms 就 ok 了。如果一个页面上的资源很多,就很浪费时间。
二. Js 和 css 的执行速度。开始的时候,我的页面都是用 js 生成 DOM ,添加样式等也用 js 添加。后来发现,加载一个页面居然要 5-6 秒。然后我就怀疑是不是 js 的执行效率不高,然后就把能用 css 的地方都用 css ,能直接写到 html 上的就不用 js 动态生成。结果,速度并没有多大的提升,最多提升了 1 秒。看来, Js 的执行速度虽然比不上 css ,但是还不至于慢到那种程度。那会是什么原因使得页面加载速度这么慢?经过仔细的排查,最终发现,是因为我用了 jQuery 框架。
Webview 加载页面的顺序是这样的:先加载 html ,然后从里面解析出 css 、 js 文件和页面上写死的图片资源进行加载,如果 webkit 的缓存里面有,就不加载。加载完这些资源之后,就进行 css 的渲染和 js 的执行。 Css 的渲染一般不需要很长时间,几十毫秒就 ok 。关键是 js 的执行,如果用了 jQuery ,则执行起来需要 5-6 秒。而在这段时间,如果不在 webview 里设置背景,网页部分是白色的,很难看。这是一个很糟糕的用户体验。所以如果用网页布局程序,最好别用很大的 js 框架。
三. 网页和 Java 之间的互调。这个功能是 iphone 里面就有的,网上也有很多资料,可以告诉我们怎么做,这些都是很简单、很基本的。我研究了一段时间,总结一下:
1. Java 调用 js 里面的函数,速度并不令人满意,大概一次一两百毫秒吧,如果要做交互性很强的事情,这种速度会让人疯掉的。而反过来就不一样了, js 去调 java 的方法,速度很快,基本上 40-50 毫秒一次。所以尽量用js 调用 java 方法,而不是 java 去调用 js 函数。
2. Java 调用 js 的函数,没有返回值,而 Js 调用 java 方法,可以有返回值。返回值可以是字符串,也可以是对象。如果是字符串,有个很讨厌的问题,第 3 点我会讲的。如果是对象,这个对象会被转换为 js 的对象,直接可以访问里面的方法。但是我不推荐 java 返回给 js 的是对象,除非是必须。因为 js 收到 java 返回的对象,会产生一些交换对象,而如果这些对象的数量增加到了 500 或 600 以上,程序就会出问题。所以尽量返回基本数据类型或者字符串。
3. Js 调用 java 的方法,返回值如果是字符串,你会发现这个字符串是 native 的,不能对它进行一些修改操作,比如想对它 substr ,取不到。怎么解决呢?转成 locale 的。使用 toLocaleString() 函数就可以了。不过这个函数的速度并不快,转化的字符串如果很多,将会很耗费时间。
四. 网页上拖动元素。网页上有一个 div ,想要拖动它到另外一个地方,怎么做?如果用 PC 上的网页做法,监听 onmousedown 、 onmousemove 和 onmouseup 就可以了。但是在手机上,事件模型就不一样了。在网页上点击,拖动,然后释放,手离开屏幕的时候, webview 才会触发 onmousedown 、 onmousemove 、onmouseup 事件。所以,要想拖动,不能这么做。这个问题困扰我很长时间,后来发现 iphone 上的做法,才解决了。 Iphone 上的 webview 有专为触摸屏设计的事件 ontouchstart 、 ontouchmove 、 ontouchend ,这几个事件的响应是实时的,就能解决拖动的问题了。
五. 一些小问题。 Webview 里面的网页,如果有 input ,需要输入,但是点上去却没反应,输入法不出来。这种情况是因为 webview 没有获取焦点。需要在 java 里面给 webview 设置一下 requestFocus() 就行了。
六. Android 上的 webview 和 iphone 的 webview 区别。目前为止,我发现的区别有这么几个:
1 . Android 上, webview 不支持多点触控,没有 ongesture 系列事件,而 iphone 上有。
2 . Android 上的 webview 不支持透明, iphone 上可以。
另外还有其他一些可重写的方法
1,接收到Http请求的事件
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
2,打开链接前的事件
public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; }
这个函数我们可以做很多操作,比如我们读取到某些特殊的URL,于是就可以不打开地址,取消这个操作,进行预先定义的其他操作,这对一个程序是非常必要的。
3,载入页面完成的事件
public void onPageFinished(WebView view, String url){ }
同样道理,我们知道一个页面载入完成,于是我们可以关闭loading条,切换程序动作。
4,载入页面开始的事件
public void onPageStarted(WebView view, String url, Bitmap favicon) { }
这个事件就是开始载入页面调用的,通常我们可以在这设定一个loading的页面,告诉用户程序在等待网络响应。
通过这几个事件,我们可以很轻松的控制程序操作,一边用着浏览器显示内容,一边监控着用户操作实现我们需要的各种显示方式,同时可以防止用户产生误操作。
Webview与js的双向交互才是android的webview强大所在,也是马甲精神能够彻底执行的基础保障。
首先,webview可以定义一个在其内嵌页面中可以触发的事件
通过以上代码,即可实现在其内嵌网页中触发window.demo.clickOnAndroid(str)事件并传参数str给webview。Webview接收到str之后,可以通过以上代码触发其内嵌页面中的js函数wave(str)。这样就可以实现网页触发webview的事件并传参数,webview接收参数并调用js函数。
下面看我的Html脚本:
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
<script type="text/javascript" src="jquery.js">script>
<script>
function toclient()
{
var order=$("#val").val();
window.demo.clickonAndroid(order);
}
function wave(str){
//alert(str.name);
$("#fromclient").text(str.name);
}
script>
head>
<body>这是一个html页面
<br/>
输入一个字符串:<br/>
<input id="val" />
<input type="submit" value="点击提交给客户端"
onclick="toclient();"/>
<br />
显示返回:<label id="fromclient">label>
body>
html>
通过脚本看到wave(str)函数是负责将原来传给webview的数据重新拿回页面,效果图如下:
另外,如果你想获取页面的一些处理数据并交给webview客户端处理,可在wave函数里将数据alert,然后webview中重写WebChromeClient的onJsAlert函数,具体代码如下
2. 特点
如下为第一篇:
基本上有两种方式来在Android上发布一个应用:作为一个客户端应用程序(使用Android SDK开发并作为一个.apk安装在用户设备上)或者作为一个Web应用(使用Web标准开发,并通过web浏览器访问——不需要在用户设备上安装什么)。
你选择以何种方式发布你的应用基于好几个因素,但是Android做出了一个决定,使得开发一个Web应用更为容易,为此,他们提供了:
因此,当你决定是否要开发一个Web应用的时候,可以不用考虑屏幕支持,因为现在已经很容易在各种由Android支持的屏幕上让Web页面看起来很合适了。
Android另外一个很好的特性是你不需要单纯在客户端或者在Web端构建你的应用。你可以将两者结合起来,开发一个Android应用,并嵌入一些Web页面(在Android应用布局中使用WebView)。 Figure 1给出了图示,阐述了你的Web页面既可以从浏览器上访问又可以从Android应用上访问的方式。然而,你不应该开发一个Android应用而仅仅将它作为一个加载你网站的方式。相反,你在Android应用中嵌入的页面应该是特意为这个环境设计的。你甚至可以在你的Android应用和你的网页间定义一个接口来让网页中的Javascript可以调用你的Android应用中的APIs——为你的基于Web的应用提供Android APIs。
从Android 1.0开始,Android应用就可以使用WebView 来在布局中嵌入网页内容并将Javascript绑定到Android APIs上去。在Android增加了对更多屏幕分辨率的支持以后(增加了对高分辨率和低分辨率屏幕的支持),Android2.0在Webkit框架中增加了一些特性,让Web页面可以指定viewport属性并查询屏幕分辨率以便修订风格和图片属性,也就是刚刚上面所提到的。因为这些特性是 Android的Webkit框架的一部分,Android浏览器(由该平台提供的默认浏览器)和WebView 都支持相同viewport以及屏幕分辨率特性。
为了为由Android驱动的设备开发一个Web应用,你应该阅读下面的文档:
为Web Apps设置合适大小
如何在Android设备上将你的Web应用设置为合适大小并支持多分辨率。当你在构建一个希望能在Android设备上可用的应用时(对于任何一个你要发布在Web上的东西,你都要做这个假设),这个文档中的信息是非常重要的,尤其是你的目标是移动设备或者使用WebView的时候。
在WebView中构建Web Apps
如何利用WebView在你Android应用中嵌入网页,并将Javascript绑定到Android APIs。
调试Web Apps
如何使用JavaScript Console APIs调试Web应用。
Web Apps的最佳惯例
一系列你需要遵循的惯例,以便为Android设备提供有效的Web应用。
在WebView的设计中,不是什么事都要WebView类干的,有些杂事是分给其他人的,这样WebView专心干好自己的解析、渲染工作就行了。WebViewClient就是帮助WebView处理各种通知、请求事件的,具体来说包括:
onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest
WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等
onCloseWindow(关闭WebView)
onCreateWindow()
onJsAlert (WebView上alert是弹不出来东西的,需要定制你的WebChromeClient处理弹出)
onJsPrompt
onJsConfirm
onProgressChanged
onReceivedIcon
onReceivedTitle
Android的webView很强大,其实就是一个浏览器,你可以把它嵌入到你想要的位置,我这里遇到两个问题,就是怎么知道网页的加载进度和加载网页时,点击网页里面的链接还是在当前的webview里跳转,不想跳到浏览器那边,解决办法如下:
public class MyWebViewClient extends WebViewClient{ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } } public class MyWebChromeClient extends WebChromeClient{ @Override public void onProgressChanged(WebView view, int newProgress) { if(newProgress==100){ view.setVisibility(View.VISIBLE); progressBar.setVisibility(View.INVISIBLE); } } } public MyWebViewClient webViewClient=new MyWebViewClient(); public MyWebChromeClient webChromeClient=new MyWebChromeClient();
webView.setWebChromeClient(webChromeClient); webView.setWebViewClient(webViewClient);
如果你在为Android开发Web应用或者在为移动设备重新设计一个Web应用,你需要仔细考虑在不同设备上你的页面看起来是怎样的。因为Android设备有不同款型,因此你需要考虑影响你的页面在Android设备上展示的一些因素。
注意: 该文档中考虑的特性只被Android 2.0 以及更高版本上的Android Browser application(由默认Android平台提供的)和WebView(用以展现web页面的框架工具集)支持。在Android上运行的第三方浏览器可能并不支持这些用来控制viewport和分辨率的特性。
当为Android设备设计页面时,有两个需要考虑的基本因素:
Viewport的大小以及web page的规模 |
当Android Browser加载一个页面的时候,默认是以”overview mode”加载,以提供一个放大的页面。你可以通过定义viewport的默认尺寸或者是viewport的初始规模来改写这一行为。你同样可以控制用户放大或缩小页面的程度。用户也可以在浏览器设置中屏蔽overview mode,这样的情况下你就不应该假设你的页面是以overview mode加载的。相反,你应该为你的页面定制合适的viewport大小和规模。
然而,当你的页面在WebView中展现的时候,页面是以最大化形式加载的(而不是”overview mode”)。也就是说,它是以页面默认大小展示的,而不是放大以后的页面(即使用户屏蔽了overview mode,页面也是如此展示)。
设备屏幕的分辨率
Android设备的屏幕分辨率会影响web页面展现的分辨率和展现大小。(有三种屏幕分辨率:低、中、高。)Android浏览器和WebView通过缩放页面来适应不同屏幕分辨率,这样所有的设备都是以默认大小即中分辨率的大小来展示web页面的。如果在你的web页面中,图像是很重要的一部分,那么你就需要密切关注在不同分辨率下发生的缩放,因为图像缩放可能会带来模糊以及像素化的问题。
为了在所有分辨率下都能提供最好的视觉效果,你需要通过提供你的页面的目标分辨率的viewport元数据来控制缩放,并通过使用CSS或者Javascript来为不同分辨率提供不同图像。
这篇文档剩下的部分讲述了你该如何考虑这些影响并为不同类型的屏幕提供一个好的设计。
Viewport是指用以展现你的页面的区域。尽管viewport的可见区域和屏幕大小是匹配的,但是它有着自己的尺寸(dimensions),这一尺寸决定了页面上可见的像素点。也就是说,一个web页面在扩张到整个屏幕之前占用的像素数据是由viewport的尺寸(dimensions)来定义的,而不是设备屏幕的尺寸。例如,尽管一个设备的屏幕宽480像素,但是viewport宽800像素,那么这个web页面需要在800像素宽的屏幕上才能完全展现。
你可以在HTML中使用 tag(这个tag必须包含在文档的
中)来为你的页面定义viewport的性质。你可以在 tag的content 属性中,定义多个viewport性质。例如,你可以定义viewport的高和宽,页面的最初大小,以及目标屏幕分辨率。content 属性中的每个viewport性质必须以逗号相隔。例如,下面的HTML片段指定了viewport宽度必须严格和屏幕宽度匹配,并禁用了放大功能:
- <head>
- <title>Exampletitle>
- <meta name=”viewport” content=”width=device-width, user-scalable=no” />
- head>
这是个定义两个viewport性质的例子。下面的语法显示了所有受支持的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]
- " />
下面的部分讨论了如何使用这些viewport性质以及可以赋给这些性质的值到底是怎样。
Figure 1. 一个web页面,其中有320像素宽的图像,在Android Browser中呈现,没有设置viewport元数据(开启了"overview mode",viewport默认为800像素宽)
Viewport的height 和 width性质让你可以指定viewport大小(即页面在扩张到屏幕之前可见的大小)。
跟上面提到的一样,Android Browser默认以”overview mode”加载页面(除非这一模式被用户禁用),将最小的viewport宽度定义为800像素。因此,如果你的web页面定义的宽度为320像素的话,那么你的页面看起来就比屏幕小(除非你的物理屏幕是320像素宽的,因为viewport模拟出了一个800像素宽的可绘图区域),就如figure 1中所示。为避免这一影响,你需要显式定义viewport的width与你设计的web页面的宽度匹配。
例如:如果你的web页面是设计为320像素宽的,那么你就需要为viewport的width指定相同大小如下:
- <meta name="viewport" content="width=320" />
在这个例子中,你的web页面和屏幕宽度大小刚好是匹配的,因此页面宽度和viewport的width是一致的。
注意: 大于10,000的width值将被忽略,小于或等于320的值将会使得width的值等于设备的宽度(下面将会讨论)。大于10,000或者小于200的height值将被忽略。
为了展现这个性质是如何影响页面大小的,figure 2展示了一个web页面,在这里,web页面中包含一个320像素宽的图像,但是viewport的width设置为400.
注意:如果你设置viewport的width与页面宽度匹配而设备屏幕大小和这些尺寸不匹配的话,web页面将仍然占满整个屏幕,即使设个设备屏幕是低分辨率或者高分辨率的,因为Android Browser和WebView 默认将web页面缩放到中等分辨率屏幕大小(如同你在figure 2中看到的一样,图中对比了高分辨率和中等分辨率设备)屏幕分辨率在Defining the viewport target density中有更多讨论。
Figure 2. 设置viewport width=400 ,开启 "overview mode" (页面中的图像为 320 像素宽).
除了将viewport尺寸定义为精确的数值以外,你还可以将其设置为永远和设备屏幕尺寸匹配,即将viewport的height和width分别赋值为device-height 和device-width。这在你开发一个有着活动大小的web应用的时候是非常合适的,这能使这个web应用的页面好像是固定的(和每个屏幕宽度都精确匹配)。例如:
- <meta name="viewport" content="width=device-width" />
Figure 3. 设置 viewport width=device-width or initial-scale=1.0的web页面.
设置viewport尺寸永远和屏幕尺寸匹配结果如figure 3所示。需要注意的是,这样的设置会导致图片缩放到与屏幕匹配,即使当前设备和target density(默认情况下是中等分辨率)并不匹配。因此,figure 3中的高分辨率设备的图片在中等分辨率的设备上放大了,以便和屏幕宽度匹配。
注意:如果你希望device-width 和 device-height和设备的物理屏幕的像素匹配,而不是通过缩放web页面来和target density匹配,那么你就必须包含一个target-densitydpi性质并将其赋值为 device-dpi。这在Defining the viewport density中将会有更多讨论。否则,只使用device-width 和 device-height来定义viewport大小的话会让你的页面自动适应每个屏幕,但是你的图片也会缩放以便适应不容屏幕分辨率。
Viewport规模确定了页面的缩放程度。Viewport性质能让你以下面的方式指定页面缩放程度:
初始缩放(initial-scale)
即页面初始缩放程度。这是一个浮点值,是页面大小的一个乘数。例如,如果你设置初始缩放为“1.0”,那么,web页面在展现的时候就会以target density分辨率的1:1来展现。如果你设置为“2.0”,那么这个页面就会放大为2倍。
默认的初始缩放值是通过计算让页面和viewport大小匹配。因为默认viewport宽度是800像素,如果设备屏幕分辨率宽度小于800,那么初始缩放值在默认情况下是小于1.0的,以便和屏幕上的800像素宽的页面匹配。
最小缩放(minimum-scale)
即允许的最小缩放程度。这是一个浮点值,用以指出页面大小与屏幕大小相比的最小乘数。例如,如果你将这个值设置为“1.0”,那么这个页面将不能缩小,因为最小值和 target density为1:1的关系。
最大缩放(maximum-scale)
即允许的最大缩放程度。这也是一个浮点值,用以指出页面大小与屏幕大小相比的最大乘数。例如,如果你将这个值设置为“2.0”,那么这个页面与target size相比,最多能放大2倍。
用户调整缩放(user-scalable)
即用户是否能改变页面缩放程度。如果设置为yes则是允许用户对其进行改变,反之为no。默认值是yes。如果你将其设置为no,那么minimum-scale 和 maximum-scale都将被忽略,因为根本不可能缩放。
所有的缩放值都必须在0.01–10的范围之内。
例如:
- <meta name="viewport" content="initial-scale=1.0" />
这个元数据将初始缩放值定义为和viewport的target density相比为满屏。
一个屏幕像素密度是由屏幕分辨率决定的,通常定义为每英寸点的数量(dpi)。Android支持三种屏幕像素密度:低像素密度,中像素密度,高像素密度。一个低像素密度的屏幕每英寸上的像素点更少,而一个高像素密度的屏幕每英寸上的像素点更多。Android Browser和WebView默认屏幕为中像素密度。
因为默认target density是中像素密度,因此当用户拥有一个低像素或者高像素密度的屏幕时,Android Browser和 WebView会缩放页面,以便它们能在中等像素密度的屏幕以合适的大小展示。更具体来说,Android Browser和 WebView会在高像素密度设备上将页面放大约1.5倍(因为高像素密度设备上的像素点更小),而在低像素密度设备上将页面缩小为约0.75倍(因为低像素密度设备上的像素点更大)。
由于默认缩放,figure 1,2,3展现了同样物理大小的web页面在高像素密度设备和中等像素密度设备上的效果(高像素密度设备上的web页面放大到实际的1.5倍,以便和target density匹配)。这会给图像带来一些问题。比如,尽管一个图像在中等像素密度和高像素密度设备上看起来大小一样,但是高像素密度设备上的图像看起来更为模糊,因为这个图像本来是为320像素宽而设计的,但却被拉到了480像素宽。
Figure 4. 设置 viewport width=device-width , target-densitydpi=device-dpi的web页面.
你可以通过使用viewport的target-densitydpi性质来改变目标屏幕像素密度。可以赋给它的值如下所列:
例如,为了防止Android Browser和WebView 根据不同屏幕的像素密度对你的页面进行缩放,你可以将viewport的target-densitydpi 设置为 device-dpi。当你这么做了,页面将不会缩放。相反,页面会根据当前屏幕的像素密度进行展示。在这种情形下,你还需要将viewport的width定义为与设备的width匹配,这样你的页面就可以和屏幕相适应。例如:
- <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
Figure 4 展示了使用这些设置的一个web页面——在高像素密度设备上,这个页面看起来小一些了,因为它的物理像素点比中等像素密度设备上的像素点要小,而又没有缩放发生,因此320像素宽的图像在两个界面上都只占用了320像素宽。(如果你想要根据屏幕像素密度来定制你的web页面的话,你就应该如此定义viewport,并使用CSS 或者 JavaScript来为不同像素密度设备提供不同图像。)
Android Browser和WebView支持一个CSS的media特性,让你能为特定像素密度的设备来创建styles——这个media特性就是 -webkit-device-pixel-ratio CSS media feature。你赋给这个特性的值应该是”0.75″, “1″, 或 “1.5″,来分别指出styles是针对低像素密度、中等像素密度和高像素密度的。
例如:你可以为不容像素密度创建样式列表stylesheets如下:
- <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" />
Figure 5. 使用CSS中的 -webkit-device-pixel-ratio 来为不同分辨率的屏幕指定不同web页面。注意在hdpi设备中使用的是一幅不同的图片。
或者,在一个样式表中指定不同的styles:
- #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);
- }
- }
注意:#header默认的style是将图片应用于中等像素密度的设备,以支持Android2.0以下的设备,这些设备是不支持-webkit-device-pixel-ratio的。
根据你设置的viewport性质不同,你要调整的对不同像素密度的styles的风格也应该不同。为了让你的页面能在不同像素密度下都有合适的styles,你需要将viewport的宽度设置为与设备匹配。即:
- <meta name="viewport" content="target-densitydpi=device-dpi, width=device-width" />
通过这种方式,Android Browser和 WebView就不会对你的页面进行缩放,并且viewport的width能与设备的width精确匹配。这一设置效果如figure 4所示。然而,通过使用-webkit-device-pixel-ratio ,你可以应用不同的styles。例如,在figure 5中,展示了一个使用如上viewport设置并使用了一些CSS的页面,在这个CSS中,定义将高分辨率的图像用于高像素密度的屏幕。
Android Browser和 WebView支持一个文档对象模型(DOM)特性,可以让你查询当前设备的像素密度——即DOM的window.devicePixelRatio 特性。这个特性的值指定了当前设备的缩放因子。例如,如果window.devicePixelRatio的值是“1.0”,则这个设备是一个中等像素密度的设备,默认不缩放;如果window.devicePixelRatio的值是“1.5”,则这个设备是一个高像素密度的设备,默认以1.5倍缩放;如果window.devicePixelRatio的值是“0.75”,则这个设备是一个低像素密度的设备,默认以0.75倍缩放。当然,Android Browser 和WebView 是根据页面的target density进行缩放的,和上文讨论的一样,其默认target是中等像素密度,但是你可以修改这个target,调整你的页面在不同屏幕分辨率下的缩放方式。
例如:你可以像下面这样通过Javascript来查询设备像素密度:
- 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");
- }
原文链接:Targeting Screens from Web Apps
将 WebView 加入你的应用
¨ 在Webview中使用 JavaScript
¨ 启用 JavaScript
¨ 将 JavaScript代码绑定到Android代码
处理页面导航
¨ 历史记录导航
WebView
WebSettings
WebViewClient
相关手册
Web View
如果你想发布一个web app(或者仅仅是一个web页面)作为客户端的一部分,你可以使用WebView。WebView是Android中 View的扩展,能让你将web页面作为你的活动布局(activity layout)。它不包含一个浏览器的完整功能,比如导航控制或者地址栏。 WebView默认做的仅仅是展现一个Web页面。
使用 WebView的一个常见场景是当你想要在应用中提供一些你可能需要更新的信息的时候,比如终端用户协议或者用户指南。在你的Android应用中,你需要创建包含WebView的Activity ,然后利用它来展现你挂在网上的文档。
另外一个使用WebView的场景是你为用户提供的数据时需要连接网络来获取数据,比如email。在这种情况下,你可能会发现在Android应用中构建一个WebView来展现提供相关数据的web页面更为容易,而不是试图连接到网络获取数据,解析数据并将其安置到Android布局中。你可以设计一个专供Android设备使用的web页面,并在Android中实现一个WebView来加载这个页面。
该文档展示了你可以如何开始使用 WebView并额外做一些事情,比如页面导航、将web页面中的Javascript代码绑定到你的Android应用中的代码上去。
将 WebView 加入你的应用
要在你的应用中加入WebView ,只需要在你的活动布局中加入元素即可。例如,下面是一个布局文件,在这个文件中,WebView 占满了屏幕。
- xml version="1.0" encoding="utf-8"?>
- <WebView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/webview"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- />
要在 WebView加载页面, 使用 loadUrl()。例如:
- WebView myWebView = (WebView) findViewById(R.id.webview);
- myWebView.loadUrl("http://www.example.com");
在它有效工作之前,你要保证你的应用能访问网络。要访问网络,需要在你的配置文件中获取INTERNET许可。例如:
- <manifest ... >
- <uses-permission android:name="android.permission.INTERNET" />
- ...
- manifest>
这就是你要应用一个WebView 来展现web页面基本的所要做的所有事情了。
在WebView中使用JavaScript
如果你想要你加载在WebView中的web页面使用Javascript,你需要在WebView中启用Javascript。一旦启用Javascript,你就可以在你的应用代码以及你的Javascript代码间创建接口了。
启用JavaScript
你可以通过WebView中带有的 WebSettings来启用它。你可以通过 getSettings()来获取 WebSettings的值,然后通过setJavaScriptEnabled()来启用JavaScript。
例如:
- WebView myWebView = (WebView) findViewById(R.id.webview);
- WebSettings webSettings = myWebView.getSettings();
- webSettings.setJavaScriptEnabled(true);
WebSettings 还提供了很多其他有用的设置。比如,如果你在开发一个专用于Android应用中 WebView 的web app,那么你就可以通过 setUserAgentString()定义自定义用户代理字符串(custom user agent string),然后通过在web页面中查询自定义用户代理来确认正在请求你的web页面的客户端确实是Android应用。
将JavaScript 代码绑定到Android 代码
在开发专用于Android应用中 WebView 的web app时,你可以在你的Javascript代码和客户端的Android代码间创建接口。例如,你的Javascript代码可以调用Android代码中的方法来展示一个Dialog,而不是使用Javascript中的alert()函数。
为了在你的Javascript和Android代码间绑定一个新的接口,需要调用addJavascriptInterface(),传给它一个类实例来绑定到Javascript,以及一个接口名让Javascript可以调用以便来访问类。
例如:你可以在你的Android应用中包括如下类:
- public class JavaScriptInterface {
- Context mContext;
- /** Instantiate the interface and set the context */
- JavaScriptInterface(Context c) {
- mContext = c;
- }
- /** Show a toast from the web page */
- public void showToast(String toast) {
- Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
- }
- }
在这个例子中,JavaScriptInterface让web页面可以使用showToast()方法来创建一个Toast 消息。
你可以通过 addJavascriptInterface()绑定这个类到在WebView 运行的Javascript,并将接口命名为Android。例如:
- WebView webView = (WebView) findViewById(R.id.webview); webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
这段代码为在WebView 运行的Javascript创建了一个名为Android的接口。这时候,你的web app就能访问JavaScriptInterface 类了。例如,下面是一些HTML以及Javascript,在用户敲击按钮的时候,它们使用这个新接口创建一个toast消息:
- <input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
- <script type="text/javascript">
- function showAndroidToast(toast) {
- Android.showToast(toast);
- }
- script>
没有必要从Javascript初始化Android接口, WebView会自动让它可以为你的web页面所用。所以,在按下按钮的时候,showAndroidToast() 函数会用这个Android接口来调用 JavaScriptInterface.showToast() 方法。
注意:绑定到你的Javascript的对象在另一个线程中运行,而不是在创建它的线程中运行。
小心:使用 addJavascriptInterface()可以让Javascript控制你的Android应用。这是一把双刃剑,有用的同时也可能带来安全威胁。当WebView 中的HTML不可信时(例如,HTML的部分或者全部都是由一个未知的人或者进程提供的),那么一个攻击者就可能使用HTML来执行客户端的任何他想要的代码。因此,不应该使用addJavascriptInterface() ,除非WebView中的所有HTML以及Javascript都是你自己写的。你同样不应该让用户在你的WebView可以定向到另外一个不是你自己的web页面上去(相反,让用户的默认浏览器应用打开外部链接——用户浏览器默认打开所有URL链接,因此一定要小心处理页面导航,像下面一节所描述的那样。)
处理页面导航
当用户点击一个WebView中的页面的链接时,默认是让Android启动一个可以处理URL的应用。通常,是由默认的浏览器打开并加载目标URL的。然而,你可以在 WebView中覆盖这一行为,那么链接就会在WebView中打开。这样,你就可以让用户通过保存在WebView中的浏览记录前进或者后退了。
要想让用户可以通过点击打开链接,只需要使用 setWebViewClient()为WebView提供一个 WebViewClient即可。例如:
- WebView myWebView = (WebView) findViewById(R.id.webview);
- myWebView.setWebViewClient(new WebViewClient());
这样就可以了。现在所有用户点击的链接都会直接在WebView中加载了。
如果你想要对于加载的链接的位置有更多控制,你可以创建自己的WebViewClient,覆盖 shouldOverrideUrlLoading()方法。例如:
- private class MyWebViewClient extends WebViewClient {
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (Uri.parse(url).getHost().equals("www.example.com")) {
- // This is my web site, so do not override; let my WebView load the page
- return false;
- }
- // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(intent);
- return true;
- }
- }
然后为 WebView创建一个新的 WebViewClient的实例。
- WebView myWebView = (WebView) findViewById(R.id.webview);
- myWebView.setWebViewClient(new MyWebViewClient());
现在当用户点击链接的时候,系统会调用shouldOverrideUrlLoading(),来检查URL host是否和某个特定的域匹配(如上面的定义)。如果匹配,那么该方法就返回false,不去覆盖URL加载(它仍然让WebView 像往常一样加载URL)。如果不匹配,那么就会创建一个Intent来加载默认活动(default Activity)来处理URLs(通过用户默认的web浏览器解析)。
历史记录导航
当你的 WebView覆盖了URL加载,它会自动生成历史访问记录。你可以通过 goBack() 或 goForward()向前或向后访问已访问过的站点。
例如,下面的代码实现了通过 Activity来利用设备的后退按钮来向后导航:
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- // Check if the key event was the BACK key and if there's history
- if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack() {
- myWebView.goBack();
- return true;
- }
- // If it wasn't the BACK key or there's no web page history, bubble up to the default
- // system behavior (probably exit the activity)
- return super.onKeyDown(keyCode, event);
- }
如果有历史访问记录可供访问,canGoBack() 方法会返回true。类似地,你可以使用canGoForward()来检查是否有向前访问历史。如果你不做这个检查,那么一旦用户访问到历史记录最后一项,goBack() 或 goForward()什么都不会做。
调试
如果你是在为Android开发web应用,那么,你可以使用控制台(console)的JavaScript API(the console JavaScript APIs)来调试你的Javascript代码并将信息输出到logcat。如果你对使用Firebug 或 Web Inspector调试web页面比较熟悉,那么,你对使用console(比如console.log())也应该比较熟悉了。Android的Webkit框架支持大多数同样的API,因此在Android的浏览器中或者WebView中调试的时候,你可以接收来自于web页面的logs。
在Android Browser中使用控制台API
Logcat
Logcat是一个工具,用来转储(dump)系统信息日志。这些信息包括设备抛出错误时的堆栈路径,以及你的应用写下的日志信息和使用JavaScript console API写下的日志信息。
要运行logcat并查看信息,从你的Android SDK tools/目录执行adb logcat ,或者从DDMS选择 Device > Run logcat。当使用 ADT plugin for Eclipse时,你同样可以通过打开Logcat view来查看logcat信息,打开途径是 Window > Show View > Other > Android > Logcat.。
在 Debugging你可以获取更多关于
当你调用一个console函数(在DOM的window.console对象中),输出会出现在logcat中。例如:如果你的web页面执行了下面的Javascript:
console.log("Hello World"); |
那么logcat 信息看起来就是类似于下面的样子:
Console: Hello World http://www.example.com/hello.html :82 |
在各个信息的格式根据Android版本的不同可能看起来会有不同。在Android 2.1及更高,来自于Android Browser的console信息会标记为”browser”。在Android 1.6及更低版本,AndroidBrowser信息则是标记为”WebCore”。
Android的WebKit并没有实现在桌面版浏览器中所实现的所有console API。但是,你可以使用下面的基本的文本日志函数(text logging function):
console.log(String) console.info(String) console.warn(String) console.error(String) |
其他一些console函数不产生错误,但是它的行为与你在其他web浏览器中预期的行为可能不一样。
在WebView中使用控制台API
如果你在应用中实现了一个定制的WebView,那么,当你在WebView中调试你的web页面的时候,所有相同的console API也是被支持的。在Android 1.6及更低版本,console信息是自动发送给logcat的,并加上了”WebCore”日志标签。如果你是为Android 2.1(API Level 7)及更高版本开发,那么就必须提供一个实现了onConsoleMessage() 回调方法的WebChromeClient,以便让console信息显示在logcat中。
另外,在API Level 7中引入的onConsoleMessage(String, int, String)方法已经弃用了,而在API Level 8中使用的是onConsoleMessage(ConsoleMessage)。
无论你是在为Android 2.1(API Level 7) 或 Android 2.2 (API Level 8 或更高)开发,你都需要实现WebChromeClient 并覆盖onConsoleMessage() 回调方法。然后,使用setWebChromeClient()将WebChromeClient应用到你的WebView 中。
如果是使用 API Level 7,那么是使用 onConsoleMessage(String, int, String)的代码看起来可能是下面这个样子:
- 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);
- }
- });
如果是使用API Level 8或更高版本, 那么你使用 onConsoleMessage(ConsoleMessage)代码看起来可能是下面的样子:
- 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来指示出发送的console 信息的类型。你可以通过 messageLevel()来查询信息层次(message level),以便确定信息重要性,然后再使用合适的Log方法或采用其他合适的行动。
无论你是使用 onConsoleMessage(String, int, String) 还是 onConsoleMessage(ConsoleMessage),当你在web页面中执行一个console方法时,Android会调用合适的 onConsoleMessage()方法,以便你能报告错误。例如,采用上面的示例代码,一个logcat信息打印出来可能是下面这个样子的:
Hello World -- From line 82 of http://www.example.com/hello.html |
1. 将来自移动设备的请求重定向到专门为移动端设计的Web版本
有好几种方式可以让你使用服务端的重定向将请求重定向到你的web站点的移动版本上去。最常见的情况是通过“嗅探”(”sniffing”)web浏览器提供的用户代理字符串(User Agent string)。为了确定是否要提供一个你的站点的移动版本,你只需要在User Agent中寻找“mobile”字符串即可,它可以与很多移动设备匹配。如果需要的话,你还可以在用户代理字符串中找到特定的操作系统(比如Android 2.1)。
注意:大屏幕的Android设备需要全尺寸的网站(比如平板电脑),因此在user agent中不包含“mobile”字符串,但它的user agent的其他部分基本是一样的。由于这种情况的存在,你在发送你的站点的移动端页面的时候,一定要确认user agent中是否包含“string”字符串。
2. 使用合适于移动设备的有效的DOCTYPE标记
为移动站点设计的最常见的标记语言是 XHTML Basic。这一标准确保了特定的标记可以在移动设备上有最佳表现。例如,它不支持HTML框架或是嵌套表格,因为这些在移动设备上表现都很糟。除了声明DOCTYPE,还要声明合适的字符编码(比如UTF-8)。
例如:
- xml version=”1.0″ encoding=”UTF-8″?>
- “http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd”>
一定要确认你的web页面的标记对于已经声明的DOCTYPE来说是有效的。可以使用验证器(validator),比如由http://validator.w3.org提供的一个。
3. 使用viewport 元数据来将页面调整至合适大小
在你的文档的部分,你需要提供元数据来指定你希望如何在浏览器的viewport中呈现你的页面。例如,你的viewport的元数据可以指定浏览器的viewport的高和宽,最初的web页面规模甚至是目标屏幕分辨率(target screen density)。
例如:
- <meta name=”viewport” content=”width=device-width, initial-scale=1.0, user-scalable=no”>
想要获取更多关于如何为Android设备使用viewport元数据的信息,请阅读Targeting Screens from Web Apps。
4. 避免多文件请求
因为移动设备通常比台式机的联网速度慢得多,因此你需要想办法让你的页面尽快加载。一种加速加载方法是避免下载额外文件,比如中的样式表和脚本文件。相反,直接在部分提供CSS或者Javascript即可(如果脚本是在页面加载完以后才用到,那么将其包含在的结束部分也可)。同样,你还可以通过类似于Minify的工具来压缩你的文件。
5. 使用一个垂直的线性布局(Use a vertical linear layout)
在导航页面的时候,要避免让用户左右拖动你的页面。对于用户来说,上下拖动更容易,也让你的页面更简单。
要想知道更多关于如何创建伟大的移动web应用,请看W3C的移动设备最佳实践。要看其他提升站点速度的建议(移动设备和桌面设备都适用),请看Yahoo!的最佳性能 指南以及Google在让web 更快的加速教程。
//session 缓存的处理
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeSessionCookie();//移除sessino缓存 ,因为该操作另开线程执行,故当前线程可能需要等待2s 直到session被清除,再重新设置。 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } cookieManager.setAcceptCookie(true); String mSessionValue = getSessionValue(sUrl);//获取session值 String cookieString = "JSESSIONID=" + mSessionValue + " ; domain=oa.myemsp.cn"; Trace.Log("cookieString : "+cookieString); cookieManager.setCookie("oa.myemsp.cn", cookieString);//设置session值 cookieSyncManager.sync();
四、使用 LoadData 方法加载内容
可以在Java文件中或者XML文件中定义HTML的片段,也可以在assets目录中存放HTML文件,然后使用LoadData加载其中的内容,展示出来。下面我们使用第三部分的网页来演示一下如何使用LoadData方法,并且看看他们之间的区别。
1、新建项目Lesson29_WebView2
2、新建2个Html文件在assets/html下,内容略去,res/layout/main.xml的内容也略去了,相信对你来说已经不成问题。
3、MainWebView2.java 的内容如下:
01 |
package android.basic.lesson29; |
02 |
03 |
import java.io.IOException; |
04 |
import java.io.InputStream; |
05 |
06 |
import org.apache.http.util.ByteArrayBuffer; |
07 |
import org.apache.http.util.EncodingUtils; |
08 |
09 |
import android.app.Activity; |
10 |
import android.os.Bundle; |
11 |
import android.view.View; |
12 |
import android.webkit.WebView; |
13 |
import android.widget.Button; |
14 |
15 |
public class MainWebView2 extends Activity { |
16 |
/** Called when the activity is first created. */ |
17 |
@Override |
18 |
public void onCreate(Bundle savedInstanceState) { |
19 |
super .onCreate(savedInstanceState); |
20 |
setContentView(R.layout.main); |
21 |
22 |
//定义UI组件 |
23 |
Button b1 = (Button) findViewById(R.id.Button01); |
24 |
Button b2 = (Button) findViewById(R.id.Button02); |
25 |
26 |
final WebView wv = (WebView) findViewById(R.id.WebView01); |
27 |
28 |
//定义并绑定按钮单击监听器 |
29 |
b1.setOnClickListener( new View.OnClickListener() { |
30 |
@Override |
31 |
public void onClick(View v) { |
32 |
//加载URL assets目录下的内容可以用 "file:///android_asset" 前缀 |
33 |
wv.loadUrl( "file:///android_asset/html/test1.html" ); |
34 |
} |
35 |
}); |
36 |
37 |
//定义并绑定按钮单击监听器 |
38 |
b2.setOnClickListener( new View.OnClickListener() { |
39 |
@Override |
40 |
public void onClick(View v) { |
41 |
String data = "" ; |
42 |
try { |
43 |
// 读取assets目录下的文件需要用到AssetManager对象的Open方法打开文件 |
44 |
InputStream is = getAssets().open( "html/test2.html" ); |
45 |
// loadData()方法需要的是一个字符串数据所以我们需要把文件转成字符串 |
46 |
ByteArrayBuffer baf = new ByteArrayBuffer( 500 ); |
47 |
int count = 0 ; |
48 |
while ((count = is.read()) != - 1 ) { |
49 |
baf.append(count); |
50 |
} |
51 |
data = EncodingUtils.getString(baf.toByteArray(), "utf-8" ); |
52 |
} catch (IOException e) { |
53 |
e.printStackTrace(); |
54 |
} |
55 |
// 下面两种方法都可以加载成功 |
56 |
wv.loadData(data, "text/html" , "utf-8" ); |
57 |
// wv.loadDataWithBaseURL("", data, "text/html", "utf-8", ""); |
58 |
} |
59 |
}); |
60 |
61 |
} |
62 |
} |
4、运行程序单击第二个按钮,效果如下:
对比上面的例子,我们可以看到两个明显的区别,其一,图片没加载出来,其二链接失效,点击后无法加载test1.html 。
五、两个和WebView相关的重要对象:WebChromeClient 和 WebViewClient
和WebView相关的辅助对象,除了WebSettings以外还有WebChromeClient和WebViewClient。
接下来的这个例子内容比较丰富,虽然注释比较清晰,但是您一次消化起来还是比较困难的,因此您需要的是一点点耐心,多看几遍,最重要的是自己至少敲代码敲一遍。
1、在上面的例子中继续增加内容,增加 test3.html 的内容:
01 |
|
02 |
03 |
< script type = "text/JavaScript" > |
04 |
function alertFuction(){ |
05 |
var a=1; |
06 |
var b=2 |
07 |
alert(a+b); |
08 |
} |
09 |
function confirmFuction(){ |
10 |
confirm("你确定要删除吗?") |
11 |
} |
12 |
13 |