浅谈WebView的使用

来源:互联网 时间:1970-01-01

转:http://blog.csdn.net/liuhe688/article/details/6549263


WebView是Android中一个非常实用的组件,它和Safai、Chrome一样都是基于Webkit网页渲染引擎,可以通过加载HTML数据的方式便捷地展现软件的界面。使用WebView开发软件有一下几个优点:

1.可以打开远程URL页面,也可以加载本地HTML数据;

2.可以无缝的在java和javascript之间进行交互操作;

3.高度的定制性,可根据开发者的需要进行多样性定制。

下面就通过例子来介绍一下WebView的使用方法。

我们先建一个webview项目,项目结构如左图:

在这个项目中,我们会先进入MainActivity这个导航界面(上边右图),通过点击不同按钮转向不同的Activity,下面分别简单介绍一下这几个Activity的所要演示的功能:

LoadActivity:主要演示加载网络页面和WebView的一些基本设置;

CaptureActivity:主要演示WebView的截图的功能;

FileActivity:主要演示访问本地文件的功能;

JSActivity:主要演示WebView对JS的支持以及JS和Java之间的互相调用;

接下来,我们会逐一分析各个Activity的相关信息:

LoadActivity:

与之对应的布局文件为load.xml,演示效果如下:

我们在文本框中输入URL,然后点击“load”按钮,然后由WebView加载相应的页面,在加载过程中,根据加载进度更新窗口的进度条。由于布局相对简单,我们主要来看一下LoadActivity.java的代码:

[java] view plaincopy
  1. package com.scott.webview;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.view.KeyEvent;  
  6. import android.view.View;  
  7. import android.view.Window;  
  8. import android.webkit.WebChromeClient;  
  9. import android.webkit.WebSettings;  
  10. import android.webkit.WebView;  
  11. import android.webkit.WebViewClient;  
  12. import android.widget.Button;  
  13. import android.widget.EditText;  
  14.   
  15. public class LoadActivity extends Activity {  
  16.       
  17.     private WebView webView;  
  18.       
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.           
  23.         //设置窗口风格为进度条  
  24.         getWindow().requestFeature(Window.FEATURE_PROGRESS);  
  25.           
  26.         setContentView(R.layout.load);  
  27.           
  28.         webView = (WebView) findViewById(R.id.webView);  
  29.           
  30.         WebSettings settings = webView.getSettings();  
  31.         settings.setSupportZoom(true);          //支持缩放  
  32.         settings.setBuiltInZoomControls(true);  //启用内置缩放装置  
  33.         settings.setJavaScriptEnabled(true);    //启用JS脚本  
  34.           
  35.         webView.setWebViewClient(new WebViewClient() {  
  36.             //当点击链接时,希望覆盖而不是打开新窗口  
  37.             @Override  
  38.             public boolean shouldOverrideUrlLoading(WebView view, String url) {  
  39.                 view.loadUrl(url);  //加载新的url  
  40.                 return true;    //返回true,代表事件已处理,事件流到此终止  
  41.             }  
  42.         });  
  43.           
  44.         //点击后退按钮,让WebView后退一页(也可以覆写Activity的onKeyDown方法)  
  45.         webView.setOnKeyListener(new View.OnKeyListener() {  
  46.             @Override  
  47.             public boolean onKey(View v, int keyCode, KeyEvent event) {  
  48.                 if (event.getAction() == KeyEvent.ACTION_DOWN) {  
  49.                     if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {  
  50.                         webView.goBack();   //后退  
  51.                         return true;    //已处理  
  52.                     }  
  53.                 }  
  54.                 return false;  
  55.             }  
  56.         });  
  57.           
  58.         webView.setWebChromeClient(new WebChromeClient() {  
  59.             //当WebView进度改变时更新窗口进度  
  60.             @Override  
  61.             public void onProgressChanged(WebView view, int newProgress) {  
  62.                 //Activity的进度范围在0到10000之间,所以这里要乘以100  
  63.                 LoadActivity.this.setProgress(newProgress * 100);  
  64.             }  
  65.         });  
  66.           
  67.         final EditText url = (EditText) findViewById(R.id.url);  
  68.           
  69.         Button loadURL = (Button) findViewById(R.id.loadURL);  
  70.         loadURL.setOnClickListener(new View.OnClickListener() {  
  71.             @Override  
  72.             public void onClick(View v) {  
  73.                 webView.loadUrl(url.getText().toString());  //加载url  
  74.                 webView.requestFocus(); //获取焦点  
  75.             }  
  76.         });  
  77.     }  
  78. }  

可以看到,我们使用loadUrl方法加载一个url页面,然后重写WebChromeClient的onProgressChanged方法更新进度条。loadUrl方法除了能加载远程页面,还能加载本地的文件:

[java] view plaincopy
  1. //加载assets中的html文件  
  2. webView.loadUrl("file:///android_asset/index.html");  
  3. //加载sdcard中的html文件  
  4. webView.loadUrl("file:///mnt/sdcard/index.html");  

这些都会在后边的示例中使用到。

CaptureActivity:

与之对应的布局文件为capture.xml,也比较简单,它的演示效果如下:

记得在AndroidManifest.xml中加入对sdcard的写权限:

[xhtml] view plaincopy
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

截图成功后,在/mnt/sdcard目录下会生成一个当前网页的截图,如图:

我们导出一下,看看是不是当前的网页界面:

整个过程操作已经完成了,让我们来看一下CaptureActivity.java的代码:

[java] view plaincopy
  1. package com.scott.webview;  
  2.   
  3. import java.io.FileOutputStream;  
  4.   
  5. import android.app.Activity;  
  6. import android.graphics.Bitmap;  
  7. import android.graphics.Canvas;  
  8. import android.graphics.Picture;  
  9. import android.os.Bundle;  
  10. import android.util.Log;  
  11. import android.view.View;  
  12. import android.webkit.WebView;  
  13. import android.widget.Button;  
  14. import android.widget.Toast;  
  15.   
  16. public class CaptureActivity extends Activity {  
  17.       
  18.     private static final String TAG = "CAPTURE";  
  19.       
  20.     private WebView webView;  
  21.       
  22.     @Override  
  23.     protected void onCreate(Bundle savedInstanceState) {  
  24.         super.onCreate(savedInstanceState);  
  25.           
  26.         setContentView(R.layout.capture);  
  27.           
  28.         webView = (WebView) findViewById(R.id.webView);  
  29.         webView.loadUrl("http://www.baidu.com");  
  30.           
  31.         Button capture = (Button) findViewById(R.id.capture);  
  32.         capture.setOnClickListener(new View.OnClickListener() {  
  33.             @Override  
  34.             public void onClick(View v) {  
  35.                 //取得android.graphics.Picture实例  
  36.                 Picture picture = webView.capturePicture();  
  37.                 int width = picture.getWidth();  
  38.                 int height = picture.getHeight();  
  39.                 if (width > 0 && height > 0) {  
  40.                     //创建指定高宽的Bitmap对象  
  41.                     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);  
  42.                     //创建Canvas,并以bitmap为绘制目标  
  43.                     Canvas canvas = new Canvas(bitmap);  
  44.                     //将WebView影像绘制在Canvas上  
  45.                     picture.draw(canvas);  
  46.                     try {  
  47.                         String fileName = "/sdcard/webview_capture.jpg";  
  48.                         FileOutputStream fos = new FileOutputStream(fileName);  
  49.                         //压缩bitmap到输出流中  
  50.                         bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);  
  51.                         fos.close();  
  52.                         Toast.makeText(CaptureActivity.this, "CAPTURE SUCCESS", Toast.LENGTH_LONG).show();  
  53.                     } catch (Exception e) {  
  54.                         Log.e(TAG, e.getMessage());  
  55.                     }  
  56.                 }  
  57.             }  
  58.         });  
  59.     }  
  60. }  

FileActivity:

这个界面没有布局文件,在代码中完成,它将演示如何加载一段html代码,并应用刚才生成的网页截图,效果如下图:

在这个过程中,我们加载了截图,并给图加上了红色的边框,CaptureActivity.java代码如下:

[java] view plaincopy
  1. package com.scott.webview;  
  2.   
  3. import android.app.Activity;  
  4. import android.os.Bundle;  
  5. import android.webkit.WebView;  
  6.   
  7. public class FileActivity extends Activity {  
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         WebView webView = new WebView(this);  
  12.         webView.getSettings().setAllowFileAccess(true); //默认就是启用的,这里只是强调一下  
  13.         String baseURL = "file:///mnt/sdcard/";         //根URL  
  14.         String html = "<html><body>"  
  15.                     + "<h3>image from sdcard:<h3><br/>"  
  16.                     + "<img src='webview_capture.jpg' style='border:2px solid #FF0000;'/>"   
  17.                     + "</body></html>";  
  18.         //加载相对于根URL下的数据,historyUrl设为null即可  
  19.         webView.loadDataWithBaseURL(baseURL, html, "text/html", "utf-8", null);  
  20.           
  21.         setContentView(webView);  
  22.     }  
  23. }  

 如果将html文本保存成.html文件,放于/mnt/sdcard目录下,然后用以下方式加载也能达到相同的效果:

[java] view plaincopy
  1. webView.loadUrl("file:///mnt/sdcard/index.html");  

接下来是最后一个示例:JSActivity,也是最精彩的示例,演示如何在JS和Java之间通信,我们来看一下演示过程,如图:

然后谈谈我们的执行过程,我们需要在Java代码中设置WebView的一些事件的响应,比如alert、confirm以及prompt这些JS事件,让它们按照我们的要求呈现给用户,然后我们需要定义一个Java和JS之间的接口对象,当我们加载完一个html文档后,根据这个对象的接口名称就可以在文档的JS代码中轻松的调用这个接口对象的方法,执行Java代码,我们也可以在Java端执行html文档中的JS代码。下面我们将通过代码来证实这一过程:

JSActivity.java代码如下:

[java] view plaincopy
  1. package com.scott.webview;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. import android.app.Activity;  
  7. import android.app.AlertDialog;  
  8. import android.content.DialogInterface;  
  9. import android.os.Bundle;  
  10. import android.os.Handler;  
  11. import android.os.Message;  
  12. import android.util.Log;  
  13. import android.view.LayoutInflater;  
  14. import android.view.View;  
  15. import android.view.Window;  
  16. import android.webkit.JsPromptResult;  
  17. import android.webkit.JsResult;  
  18. import android.webkit.WebChromeClient;  
  19. import android.webkit.WebView;  
  20. import android.widget.EditText;  
  21. import android.widget.Toast;  
  22.   
  23. public class JSActivity extends Activity {  
  24.       
  25.     private static final String TAG = "JSActivity";  
  26.       
  27.     private  WebView webView;  
  28.       
  29.     private Handler handler = new Handler() {  
  30.         public void handleMessage(android.os.Message msg) {  
  31.             int index = msg.arg1;  
  32.             JSActivity.this.setProgress(index * 1000);  
  33.         };  
  34.     };  
  35.       
  36.     @Override  
  37.     public void onCreate(Bundle savedInstanceState) {  
  38.         super.onCreate(savedInstanceState);  
  39.           
  40.         getWindow().requestFeature(Window.FEATURE_PROGRESS);  
  41.           
  42.         webView = new WebView(this);  
  43.           
  44.         webView.getSettings().setJavaScriptEnabled(true);  
  45.           
  46.         webView.addJavascriptInterface(new Object() {  
  47.             @SuppressWarnings("unused")  
  48.             public List<String> getList() {  
  49.                 List<String> list = new ArrayList<String>();  
  50.                 for (int i = 0; i <= 10; i++) {  
  51.                     try {  
  52.                         Thread.sleep(200);  
  53.                     } catch (InterruptedException e) {  
  54.                         Log.e(TAG, "error:" + e.getMessage());  
  55.                     }  
  56.                     list.add("current index is: " + i);  
  57.                       
  58.                     //不能在此直接调用Activity.setProgress,否则会报以下错误  
  59.                     //Only the original thread that created a view hierarchy can touch its views.  
  60.                     Message msg = handler.obtainMessage();  
  61.                     msg.arg1 = i;  
  62.                     handler.sendMessage(msg);  
  63.                 }  
  64.                 success();  
  65.                 return list;  
  66.             }  
  67.               
  68.             public void success() {  
  69.                 //由Java代码调用JS函数  
  70.                 webView.loadUrl("javascript:success('congratulations')");  
  71.             }  
  72.         }, "bridge");  
  73.           
  74.         webView.setWebChromeClient(new WebChromeClient() {  
  75.             @Override  
  76.             public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {  
  77.                 new AlertDialog.Builder(JSActivity.this)  
  78.                         .setTitle("alert")  
  79.                         .setMessage(message)  
  80.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
  81.                             @Override  
  82.                             public void onClick(DialogInterface dialog, int which) {  
  83.                                 //处理结果为确定状态 同时唤醒WebCore线程  
  84.                                 result.confirm();  
  85.                             }  
  86.                         }).create().show();  
  87.                 return true;    //已处理  
  88.             }  
  89.   
  90.             @Override  
  91.             public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {  
  92.                 new AlertDialog.Builder(JSActivity.this)  
  93.                         .setTitle("confirm")  
  94.                         .setMessage(message)  
  95.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
  96.                             @Override  
  97.                             public void onClick(DialogInterface dialog, int which) {  
  98.                                 Toast.makeText(JSActivity.this, "you clicked yes", 0).show();  
  99.                                 result.confirm();  
  100.                             }  
  101.                         })  
  102.                         .setNegativeButton("NO", new DialogInterface.OnClickListener() {  
  103.                             @Override  
  104.                             public void onClick(DialogInterface dialog, int which) {  
  105.                                 //处理结果为取消状态 同时唤醒WebCore线程  
  106.                                 result.cancel();  
  107.                             }  
  108.                         }).create().show();  
  109.                 return true;  
  110.             }  
  111.               
  112.             @Override  
  113.             public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,  
  114.                     final JsPromptResult result) {  
  115.                 LayoutInflater inflater = getLayoutInflater();  
  116.                 View prompt = inflater.inflate(R.layout.prompt, null);  
  117.                 final EditText text = (EditText) prompt.findViewById(R.id.prompt_input);  
  118.                 text.setHint(defaultValue);  
  119.                   
  120.                 new AlertDialog.Builder(JSActivity.this)  
  121.                         .setTitle("prompt")  
  122.                         .setView(prompt)  
  123.                         .setPositiveButton("YES", new DialogInterface.OnClickListener() {  
  124.                             @Override  
  125.                             public void onClick(DialogInterface dialog, int which) {  
  126.                                 //记录结果  
  127.                                 result.confirm(text.getText().toString());  
  128.                             }  
  129.                         })  
  130.                         .setNegativeButton("NO", new DialogInterface.OnClickListener() {  
  131.                             @Override  
  132.                             public void onClick(DialogInterface dialog, int which) {  
  133.                                 result.cancel();  
  134.                             }  
  135.                         }).create().show();  
  136.                 return true;  
  137.             }  
  138.         });  
  139.   
  140.         //加载assets中的html文件  
  141.         webView.loadUrl("file:///android_asset/index.html");  
  142.           
  143.         setContentView(webView);  
  144.     }  
  145. }  

需要注意的是,在重写onJsAlert、onJsConfirm、onJsPrompt这几个方法中,不要忘了用JsResult.confirm()或JsResult.cancel()处理结果,否则页面就不能再响应接下的事件了,关于这一点,我们可以看一下JsResult的代码:

[c-sharp] view plaincopy
  1. /** 
  2.      * Handle a confirmation response from the user. 
  3.      */  
  4.     public final void confirm() {  
  5.         mResult = true;  
  6.         wakeUp();  
  7.     }  
  8.   
  9. /** 
  10.      * Handle the result if the user cancelled the dialog. 
  11.      */  
  12.     public final void cancel() {  
  13.         mResult = false;  
  14.         wakeUp();  
  15.     }  

可以看到confirm和cancel方法都涉及到了一个wakeUp方法,这个方法主要作用是唤醒WebCore线程,定义如下:

[c-sharp] view plaincopy
  1. /* Wake up the WebCore thread. */  
  2.     protected final void wakeUp() {  
  3.         if (mReady) {  
  4.             synchronized (mProxy) {  
  5.                 mProxy.notify();  
  6.             }  
  7.         } else {  
  8.             mTriedToNotifyBeforeReady = true;  
  9.         }  
  10.     }  

所以朋友们如果要重写这几个方法时要切记处理JsResult这个对象实例。

我们在处理onJsPrompt时,使用了自定义的界面,加载的是/res/layout/prompt.xml,定义如下:

[xhtml] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:layout_width="wrap_content"  
  5.   android:layout_height="wrap_content">  
  6.   <EditText  
  7.         android:id="@+id/prompt_input"  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"/>  
  10. </LinearLayout>  

在JSActivity.java代码中,我们注意到WebView组件加载了assets中的index.html,它包含与Java交互的JS代码,如下:

[xhtml] view plaincopy
  1. <html>  
  2.     <head>  
  3.         <script type="text/javascript">  
  4.             function doAlert() {  
  5.                 alert("hello!");  
  6.             }  
  7.   
  8.             function doConfirm() {  
  9.                 confirm("are you sure?");  
  10.             }  
  11.   
  12.             function doPrompt() {  
  13.                 var val = prompt("what's your name?");  
  14.                 if (val) {  
  15.                     alert("your name is:" + val);  
  16.                 }  
  17.             }  
  18.   
  19.             function getList() {  
  20.                 //使用java和javascript的接口bridge的方法获取集合  
  21.                 var list = window.bridge.getList();  
  22.                 var result = document.getElementById("result");  
  23.                 for (var i = 0; i < list.size(); i++) {  
  24.                     var div = document.createElement("div");  
  25.                     div.innerHTML = list.get(i).toString();  
  26.                     result.appendChild(div);  
  27.                 }  
  28.             }  
  29.   
  30.             function success(msg) {  
  31.                 alert(msg);  
  32.             }  
  33.         </script>  
  34.     </head>  
  35.     <body background="black">  
  36.         <input type="button" value="alert" onclick="doAlert()"/><br/>  
  37.         <input type="button" value="confirm" onclick="doConfirm()"/><br/>  
  38.         <input type="button" value="prompt" onclick="doPrompt()"/><br/>  
  39.         <input type="button" value="getList" onclick="getList()"/><br/>  
  40.         <div id="result"></div>  
  41.     </body>  
  42. </html>  

WebView的优点还不止这些,希望在今后的时间里,再和大家分享WebView的相关技术,也希望这篇文章能够对初识WebView的朋友们带来帮助。



相关阅读:
Top