webview

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的代碼:

  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方法除了能加載遠程頁面,還能加載本地的文件:

  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的寫權限:

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

截圖成功後,在/mnt/sdcard目錄下會生成一個當前網頁的截圖,如圖:

我們導出一下,看看是不是當前的網頁界面:

整個過程操作已經完成了,讓我們來看一下CaptureActivity.java的代碼:

  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代碼如下:

  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目錄下,然後用以下方式加載也能達到相同的效果:

  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代碼如下:

  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的代碼:

  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線程,定義如下:

  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,定義如下:

  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代碼,如下:

  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的朋友們帶來幫助。


發佈了13 篇原創文章 · 獲贊 2 · 訪問量 19萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章