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的代碼:
- package com.scott.webview;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.KeyEvent;
- import android.view.View;
- import android.view.Window;
- import android.webkit.WebChromeClient;
- import android.webkit.WebSettings;
- import android.webkit.WebView;
- import android.webkit.WebViewClient;
- import android.widget.Button;
- import android.widget.EditText;
- public class LoadActivity extends Activity {
- private WebView webView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- //設置窗口風格爲進度條
- getWindow().requestFeature(Window.FEATURE_PROGRESS);
- setContentView(R.layout.load);
- webView = (WebView) findViewById(R.id.webView);
- WebSettings settings = webView.getSettings();
- settings.setSupportZoom(true); //支持縮放
- settings.setBuiltInZoomControls(true); //啓用內置縮放裝置
- settings.setJavaScriptEnabled(true); //啓用JS腳本
- webView.setWebViewClient(new WebViewClient() {
- //當點擊鏈接時,希望覆蓋而不是打開新窗口
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- view.loadUrl(url); //加載新的url
- return true; //返回true,代表事件已處理,事件流到此終止
- }
- });
- //點擊後退按鈕,讓WebView後退一頁(也可以覆寫Activity的onKeyDown方法)
- webView.setOnKeyListener(new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
- webView.goBack(); //後退
- return true; //已處理
- }
- }
- return false;
- }
- });
- webView.setWebChromeClient(new WebChromeClient() {
- //當WebView進度改變時更新窗口進度
- @Override
- public void onProgressChanged(WebView view, int newProgress) {
- //Activity的進度範圍在0到10000之間,所以這裏要乘以100
- LoadActivity.this.setProgress(newProgress * 100);
- }
- });
- final EditText url = (EditText) findViewById(R.id.url);
- Button loadURL = (Button) findViewById(R.id.loadURL);
- loadURL.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- webView.loadUrl(url.getText().toString()); //加載url
- webView.requestFocus(); //獲取焦點
- }
- });
- }
- }
可以看到,我們使用loadUrl方法加載一個url頁面,然後重寫WebChromeClient的onProgressChanged方法更新進度條。loadUrl方法除了能加載遠程頁面,還能加載本地的文件:
- //加載assets中的html文件
- webView.loadUrl("file:///android_asset/index.html");
- //加載sdcard中的html文件
- webView.loadUrl("file:///mnt/sdcard/index.html");
這些都會在後邊的示例中使用到。
CaptureActivity:
與之對應的佈局文件爲capture.xml,也比較簡單,它的演示效果如下:
記得在AndroidManifest.xml中加入對sdcard的寫權限:
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
截圖成功後,在/mnt/sdcard目錄下會生成一個當前網頁的截圖,如圖:
我們導出一下,看看是不是當前的網頁界面:
整個過程操作已經完成了,讓我們來看一下CaptureActivity.java的代碼:
- package com.scott.webview;
- import java.io.FileOutputStream;
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Picture;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.webkit.WebView;
- import android.widget.Button;
- import android.widget.Toast;
- public class CaptureActivity extends Activity {
- private static final String TAG = "CAPTURE";
- private WebView webView;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.capture);
- webView = (WebView) findViewById(R.id.webView);
- webView.loadUrl("http://www.baidu.com");
- Button capture = (Button) findViewById(R.id.capture);
- capture.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //取得android.graphics.Picture實例
- Picture picture = webView.capturePicture();
- int width = picture.getWidth();
- int height = picture.getHeight();
- if (width > 0 && height > 0) {
- //創建指定高寬的Bitmap對象
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- //創建Canvas,並以bitmap爲繪製目標
- Canvas canvas = new Canvas(bitmap);
- //將WebView影像繪製在Canvas上
- picture.draw(canvas);
- try {
- String fileName = "/sdcard/webview_capture.jpg";
- FileOutputStream fos = new FileOutputStream(fileName);
- //壓縮bitmap到輸出流中
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
- fos.close();
- Toast.makeText(CaptureActivity.this, "CAPTURE SUCCESS", Toast.LENGTH_LONG).show();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- }
- }
- }
- });
- }
- }
FileActivity:
這個界面沒有佈局文件,在代碼中完成,它將演示如何加載一段html代碼,並應用剛纔生成的網頁截圖,效果如下圖:
在這個過程中,我們加載了截圖,並給圖加上了紅色的邊框,CaptureActivity.java代碼如下:
- package com.scott.webview;
- import android.app.Activity;
- import android.os.Bundle;
- import android.webkit.WebView;
- public class FileActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- WebView webView = new WebView(this);
- webView.getSettings().setAllowFileAccess(true); //默認就是啓用的,這裏只是強調一下
- String baseURL = "file:///mnt/sdcard/"; //根URL
- String html = "<html><body>"
- + "<h3>image from sdcard:<h3><br/>"
- + "<img src='webview_capture.jpg' style='border:2px solid #FF0000;'/>"
- + "</body></html>";
- //加載相對於根URL下的數據,historyUrl設爲null即可
- webView.loadDataWithBaseURL(baseURL, html, "text/html", "utf-8", null);
- setContentView(webView);
- }
- }
如果將html文本保存成.html文件,放於/mnt/sdcard目錄下,然後用以下方式加載也能達到相同的效果:
- 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代碼如下:
- package com.scott.webview;
- import java.util.ArrayList;
- import java.util.List;
- import android.app.Activity;
- import android.app.AlertDialog;
- import android.content.DialogInterface;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.Window;
- import android.webkit.JsPromptResult;
- import android.webkit.JsResult;
- import android.webkit.WebChromeClient;
- import android.webkit.WebView;
- import android.widget.EditText;
- import android.widget.Toast;
- public class JSActivity extends Activity {
- private static final String TAG = "JSActivity";
- private WebView webView;
- private Handler handler = new Handler() {
- public void handleMessage(android.os.Message msg) {
- int index = msg.arg1;
- JSActivity.this.setProgress(index * 1000);
- };
- };
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- getWindow().requestFeature(Window.FEATURE_PROGRESS);
- webView = new WebView(this);
- webView.getSettings().setJavaScriptEnabled(true);
- webView.addJavascriptInterface(new Object() {
- @SuppressWarnings("unused")
- public List<String> getList() {
- List<String> list = new ArrayList<String>();
- for (int i = 0; i <= 10; i++) {
- try {
- Thread.sleep(200);
- } catch (InterruptedException e) {
- Log.e(TAG, "error:" + e.getMessage());
- }
- list.add("current index is: " + i);
- //不能在此直接調用Activity.setProgress,否則會報以下錯誤
- //Only the original thread that created a view hierarchy can touch its views.
- Message msg = handler.obtainMessage();
- msg.arg1 = i;
- handler.sendMessage(msg);
- }
- success();
- return list;
- }
- public void success() {
- //由Java代碼調用JS函數
- webView.loadUrl("javascript:success('congratulations')");
- }
- }, "bridge");
- webView.setWebChromeClient(new WebChromeClient() {
- @Override
- public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
- new AlertDialog.Builder(JSActivity.this)
- .setTitle("alert")
- .setMessage(message)
- .setPositiveButton("YES", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //處理結果爲確定狀態 同時喚醒WebCore線程
- result.confirm();
- }
- }).create().show();
- return true; //已處理
- }
- @Override
- public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
- new AlertDialog.Builder(JSActivity.this)
- .setTitle("confirm")
- .setMessage(message)
- .setPositiveButton("YES", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Toast.makeText(JSActivity.this, "you clicked yes", 0).show();
- result.confirm();
- }
- })
- .setNegativeButton("NO", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //處理結果爲取消狀態 同時喚醒WebCore線程
- result.cancel();
- }
- }).create().show();
- return true;
- }
- @Override
- public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
- final JsPromptResult result) {
- LayoutInflater inflater = getLayoutInflater();
- View prompt = inflater.inflate(R.layout.prompt, null);
- final EditText text = (EditText) prompt.findViewById(R.id.prompt_input);
- text.setHint(defaultValue);
- new AlertDialog.Builder(JSActivity.this)
- .setTitle("prompt")
- .setView(prompt)
- .setPositiveButton("YES", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //記錄結果
- result.confirm(text.getText().toString());
- }
- })
- .setNegativeButton("NO", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- result.cancel();
- }
- }).create().show();
- return true;
- }
- });
- //加載assets中的html文件
- webView.loadUrl("file:///android_asset/index.html");
- setContentView(webView);
- }
- }
需要注意的是,在重寫onJsAlert、onJsConfirm、onJsPrompt這幾個方法中,不要忘了用JsResult.confirm()或JsResult.cancel()處理結果,否則頁面就不能再響應接下的事件了,關於這一點,我們可以看一下JsResult的代碼:
- /**
- * Handle a confirmation response from the user.
- */
- public final void confirm() {
- mResult = true;
- wakeUp();
- }
- /**
- * Handle the result if the user cancelled the dialog.
- */
- public final void cancel() {
- mResult = false;
- wakeUp();
- }
可以看到confirm和cancel方法都涉及到了一個wakeUp方法,這個方法主要作用是喚醒WebCore線程,定義如下:
- /* Wake up the WebCore thread. */
- protected final void wakeUp() {
- if (mReady) {
- synchronized (mProxy) {
- mProxy.notify();
- }
- } else {
- mTriedToNotifyBeforeReady = true;
- }
- }
所以朋友們如果要重寫這幾個方法時要切記處理JsResult這個對象實例。
我們在處理onJsPrompt時,使用了自定義的界面,加載的是/res/layout/prompt.xml,定義如下:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <EditText
- android:id="@+id/prompt_input"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
在JSActivity.java代碼中,我們注意到WebView組件加載了assets中的index.html,它包含與Java交互的JS代碼,如下:
- <html>
- <head>
- <script type="text/javascript">
- function doAlert() {
- alert("hello!");
- }
- function doConfirm() {
- confirm("are you sure?");
- }
- function doPrompt() {
- var val = prompt("what's your name?");
- if (val) {
- alert("your name is:" + val);
- }
- }
- function getList() {
- //使用java和javascript的接口bridge的方法獲取集合
- var list = window.bridge.getList();
- var result = document.getElementById("result");
- for (var i = 0; i < list.size(); i++) {
- var div = document.createElement("div");
- div.innerHTML = list.get(i).toString();
- result.appendChild(div);
- }
- }
- function success(msg) {
- alert(msg);
- }
- </script>
- </head>
- <body background="black">
- <input type="button" value="alert" onclick="doAlert()"/><br/>
- <input type="button" value="confirm" onclick="doConfirm()"/><br/>
- <input type="button" value="prompt" onclick="doPrompt()"/><br/>
- <input type="button" value="getList" onclick="getList()"/><br/>
- <div id="result"></div>
- </body>
- </html>
WebView的優點還不止這些,希望在今後的時間裏,再和大家分享WebView的相關技術,也希望這篇文章能夠對初識WebView的朋友們帶來幫助。