蛙撲安卓:
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的代碼:可以看到,我們使用loadUrl方法加載一個url頁面,然後重寫WebChromeClient的onProgressChanged方法更新進度條。loadUrl方法除了能加載遠端頁面,還能加載本地的檔案: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(); //擷取焦點 } }); } }
//加載assets中的html檔案 webView.loadUrl("file:///android_asset/index.html"); //加載sdcard中的html檔案 webView.loadUrl("file:///mnt/sdcard/index.html");
這些都會在後邊的示例中使用到。
CaptureActivity:
與之對應的布局檔案為capture.xml,也比較簡單,它的示範效果如下:
記得在AndroidManifest.xml中加入對sdcard的寫權限:截圖成功後,在/mnt/sdcard目錄下會生成一個目前網頁的截圖,如圖: 我們導出一下,看看是不是目前的網頁界面: 整個過程操作已經完成了,讓我們來看一下CaptureActivity.java的代碼:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
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代碼如下:如果将html文本儲存成.html檔案,放于/mnt/sdcard目錄下,然後用以下方式加載也能達到相同的效果: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); } }
接下來是最後一個示例:JSActivity,也是最精彩的示例,示範如何在JS和Java之間通信,我們來看一下示範過程,如圖:webView.loadUrl("file:///mnt/sdcard/index.html");
然後談談我們的執行過程,我們需要在Java代碼中設定WebView的一些事件的響應,比如alert、confirm以及prompt這些JS事件,讓它們按照我們的要求呈現給使用者,然後我們需要定義一個Java和JS之間的接口對象,當我們加載完一個html文檔後,根據這個對象的接口名稱就可以在文檔的JS代碼中輕松的調用這個接口對象的方法,執行Java代碼,我們也可以在Java端執行html文檔中的JS代碼。下面我們将通過代碼來證明這一過程:
JSActivity.java代碼如下:
需要注意的是,在重寫onJsAlert、onJsConfirm、onJsPrompt這幾個方法中,不要忘了用JsResult.confirm()或JsResult.cancel()處理結果,否則頁面就不能再響應接下的事件了,關于這一點,我們可以看一下JsResult的代碼: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); } }
可以看到confirm和cancel方法都涉及到了一個wakeUp方法,這個方法主要作用是喚醒WebCore線程,定義如下:/** * 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(); }
/* Wake up the WebCore thread. */ protected final void wakeUp() { if (mReady) { synchronized (mProxy) { mProxy.notify(); } } else { mTriedToNotifyBeforeReady = true; } }
是以朋友們如果要重寫這幾個方法時要切記處理JsResult這個對象執行個體。
我們在處理onJsPrompt時,使用了自定義的界面,加載的是/res/layout/prompt.xml,定義如下:
在JSActivity.java代碼中,我們注意到WebView元件加載了assets中的index.html,它包含與Java互動的JS代碼,如下:<?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>
<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" οnclick="doAlert()"/><br/> <input type="button" value="confirm" οnclick="doConfirm()"/><br/> <input type="button" value="prompt" οnclick="doPrompt()"/><br/> <input type="button" value="getList" οnclick="getList()"/><br/> <div id="result"></div> </body> </html>
轉載于:https://www.cnblogs.com/luckjun/archive/2013/03/22/2976117.html