使用 WebView
提供網頁應用程式或網頁,做為用戶端應用程式的一部分。WebView
類別是 Android View
類別的擴充功能,可讓您將網頁顯示為活動版面配置的一部分。不含完整開發網路瀏覽器的功能,例如導覽控制項或網址列。根據預設,所有 WebView
都會顯示網頁。
WebView
可協助您在應用程式中提供可能需要更新的資訊,例如使用者協議或使用手冊。在 Android 應用程式中,您可以建立包含 WebView
的 Activity
,然後用它來顯示代管在網路上的文件。
當應用程式將資料提供給需要網路連線才能擷取資料 (例如電子郵件) 的使用者提供時,WebView
也能派上用場。在這種情況下,您可能會發現在 Android 應用程式中建構 WebView
,可顯示網頁包含所有使用者資料,而不是執行網路要求,然後剖析資料並在 Android 版面配置中顯示。相反地,您可以設計專為 Android 裝置打造的網頁,然後在可載入網頁的 Android 應用程式中實作 WebView
。
本文件說明如何開始使用 WebView
、如何將網頁中的 JavaScript 與 Android 應用程式中的用戶端程式碼繫結、如何處理網頁導覽,以及如何在使用 WebView
時管理視窗。
在舊版 Android 上使用 WebView
如要在執行應用程式的裝置上安全地使用近期的 WebView
功能,請新增 AndroidX Webkit 程式庫。這是一個靜態程式庫,您可以將其新增至應用程式,以使用較舊的平台版本無法使用的 android.webkit
API。
將其新增至 build.gradle
檔案,如下所示:
Kotlin
dependencies { implementation("androidx.webkit:webkit:1.8.0") }
Groovy
dependencies { implementation ("androidx.webkit:webkit:1.8.0") }
詳情請參閱 GitHub 上的 WebView
範例。
在應用程式中新增 WebView
若要在應用程式中加入 WebView
,您可以在活動版面配置中加入 <WebView>
元素,也可以在 onCreate()
中將整個 Activity
視窗設為 WebView
。
在活動版面配置中新增 WebView
如要在版面配置中為應用程式新增 WebView
,請將下列程式碼加入活動的版面配置 XML 檔案:
<WebView android:id="@+id/webview" android:layout_width="match_parent" android:layout_height="match_parent" />
如要在 WebView
中載入網頁,請使用 loadUrl()
,如以下範例所示:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.loadUrl("http://www.example.com")
Java
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.loadUrl("http://www.example.com");
在 onCreate() 中新增 WebView
如要在活動的 onCreate()
方法中改為將 WebView
新增至應用程式,請使用類似以下的邏輯:
Kotlin
val myWebView = WebView(activityContext) setContentView(myWebView)
Java
WebView myWebView = new WebView(activityContext); setContentView(myWebView);
然後載入頁面:
Kotlin
myWebView.loadUrl("http://www.example.com")
Java
myWebView.loadUrl("https://www.example.com");
或從 HTML 字串載入網址:
Kotlin
// Create an unencoded HTML string, then convert the unencoded HTML string into // bytes. Encode it with base64 and load the data. val unencodedHtml = "<html><body>'%23' is the percent code for ‘#‘ </body></html>"; val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING) myWebView.loadData(encodedHtml, "text/html", "base64")
Java
// Create an unencoded HTML string, then convert the unencoded HTML string into // bytes. Encode it with base64 and load the data. String unencodedHtml = "<html><body>'%23' is the percent code for ‘#‘ </body></html>"; String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING); myWebView.loadData(encodedHtml, "text/html", "base64");
您的應用程式必須可以存取網際網路。如要取得網際網路存取權,請在資訊清單檔案中要求 INTERNET
權限,如以下範例所示:
<manifest ... > <uses-permission android:name="android.permission.INTERNET" /> ... </manifest>
您可以透過下列任一方式自訂 WebView
:
- 使用
WebChromeClient
啟用全螢幕支援。如果WebView
需要權限修改主機應用程式的 UI,例如建立或關閉視窗,或向使用者傳送 JavaScript 對話方塊,系統也會呼叫此類別。如要進一步瞭解如何在這種情況下進行偵錯,請參閱「對網頁應用程式偵錯」。 - 處理會影響內容轉譯的事件,例如使用
WebViewClient
處理提交或瀏覽時的錯誤。您也可以使用這個子類別來攔截網址載入作業。 - 修改
WebSettings
來啟用 JavaScript。 - 使用 JavaScript 存取已插入
WebView
的 Android 架構物件。
在 WebView 中使用 JavaScript
如果您要在 WebView
中載入的網頁使用 JavaScript,您必須為 WebView
啟用 JavaScript。啟用 JavaScript 後,即可在應用程式程式碼和 JavaScript 程式碼之間建立介面。
啟用 JavaScript
根據預設,WebView
會停用 JavaScript。您可以透過附加至 WebView
的 WebSettings
來啟用。使用 getSettings()
擷取 WebSettings
,然後使用 setJavaScriptEnabled()
啟用 JavaScript。
請參閱以下範例:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.settings.javaScriptEnabled = true
Java
WebView myWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = myWebView.getSettings(); webSettings.setJavaScriptEnabled(true);
WebSettings
可讓您存取其他許多實用的設定。舉例來說,如果您是針對 Android 應用程式中的 WebView
開發專屬網頁應用程式,可以使用 setUserAgentString()
定義自訂使用者代理程式字串,然後查詢網頁中的自訂使用者代理程式,確認要求您的網頁的用戶端是否為您的 Android 應用程式。
將 JavaScript 程式碼繫結至 Android 程式碼
在 Android 應用程式中開發專為 WebView
設計的網頁應用程式時,您可以在 JavaScript 程式碼和用戶端 Android 程式碼之間建立介面。舉例來說,JavaScript 程式碼可以在 Android 程式碼中呼叫方法來顯示 Dialog
,而不是使用 JavaScript 的 alert()
函式。
如要在 JavaScript 和 Android 程式碼之間繫結新介面,請呼叫 addJavascriptInterface()
,並傳遞用於繫結至 JavaScript 的類別例項,以及 JavaScript 可呼叫以存取類別的介面名稱。
舉例來說,您可以在 Android 應用程式中加入下列類別:
Kotlin
/** Instantiate the interface and set the context. */ class WebAppInterface(private val mContext: Context) { /** Show a toast from the web page. */ @JavascriptInterface fun showToast(toast: String) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show() } }
Java
public class WebAppInterface { Context mContext; /** Instantiate the interface and set the context. */ WebAppInterface(Context c) { mContext = c; } /** Show a toast from the web page. */ @JavascriptInterface public void showToast(String toast) { Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show(); } }
在本範例中,WebAppInterface
類別可讓網頁使用 showToast()
方法建立 Toast
訊息。
您可以使用 addJavascriptInterface()
將這個類別繫結至在 WebView
中執行的 JavaScript,如以下範例所示:
Kotlin
val webView: WebView = findViewById(R.id.webview) webView.addJavascriptInterface(WebAppInterface(this), "Android")
Java
WebView webView = (WebView) findViewById(R.id.webview); webView.addJavascriptInterface(new WebAppInterface(this), "Android");
這項操作會建立名為 Android
的介面,用於在 WebView
中執行 JavaScript 的介面。此時,網頁應用程式可以存取 WebAppInterface
類別。舉例來說,以下 HTML 和 JavaScript 會在使用者輕觸按鈕時,使用新介面建立浮動式訊息:
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" /> <script type="text/javascript"> function showAndroidToast(toast) { Android.showToast(toast); } </script>
您不需要從 JavaScript 初始化 Android
介面。WebView
會自動提供給您的網頁使用。因此,當使用者輕觸按鈕時,showAndroidToast()
函式會使用 Android
介面呼叫 WebAppInterface.showToast()
方法。
處理頁面瀏覽
根據預設,當使用者輕觸 WebView
中的網頁連結時,Android 會啟動可處理網址的應用程式。一般來說,預設的網路瀏覽器會開啟並載入到達網頁網址。不過,您可以覆寫 WebView
的這項行為,讓連結在 WebView
中開啟。這樣一來,您就可以讓使用者向後或向前瀏覽由 WebView
維護的網頁歷史記錄。
如要開啟使用者輕觸的連結,請使用 setWebViewClient()
為 WebView
提供 WebViewClient
。使用者輕觸的所有連結會在 WebView
中載入。如要進一步控管點擊連結的載入位置,請自行建立 WebViewClient
來覆寫 shouldOverrideUrlLoading()
方法。以下範例假設 MyWebViewClient
是 Activity
的內部類別。
Kotlin
private class MyWebViewClient : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { if (Uri.parse(url).host == "www.example.com") { // This is your website, so don't override. Let your WebView load // the page. return false } // Otherwise, the link isn't for a page on your site, so launch another // Activity that handles URLs. Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply { startActivity(this) } return true } }
Java
private class MyWebViewClient extends WebViewClient { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if ("www.example.com".equals(request.getUrl().getHost())) { // This is your website, so don't override. Let your WebView load the // page. return false; } // Otherwise, the link isn't for a page on your site, so launch another // Activity that handles URLs. Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl()); startActivity(intent); return true; } }
然後為 WebView
建立這個新 WebViewClient
的執行個體:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) myWebView.webViewClient = MyWebViewClient()
Java
WebView myWebView = (WebView) findViewById(R.id.webview); myWebView.setWebViewClient(new MyWebViewClient());
現在,當使用者輕觸連結時,系統會呼叫 shouldOverrideUrlLoading()
方法,以檢查網址主機是否與特定網域相符 (如上述範例所定義)。如果相符,這個方法會傳回 false,且不會覆寫網址載入。可讓 WebView
照常載入網址。如果網址主機不相符,系統會建立 Intent
來啟動預設的 Activity
來處理網址,進而解析使用者的預設網路瀏覽器。
處理自訂網址
WebView
會在要求資源及解析使用自訂網址配置的連結時套用限制。舉例來說,如果您實作回呼 (例如 shouldOverrideUrlLoading()
或 shouldInterceptRequest()
),則 WebView
只會針對有效網址叫用這些回呼。
舉例來說,WebView
可能不會針對以下連結呼叫 shouldOverrideUrlLoading()
方法:
<a href="showProfile">Show Profile</a>
系統以不一致的方式在 WebView
中處理無效網址,就像上述範例中顯示的網址一樣,因此建議您改用格式正確的網址。您可以針對貴機構控制的網域使用自訂配置或 HTTPS 網址。
如同上述範例,您可以不如上述範例,在連結中使用簡易字串,如下所示:
<a href="example-app:showProfile">Show Profile</a>
接著,您可以在 shouldOverrideUrlLoading()
方法中處理這個網址,如下所示:
Kotlin
// The URL scheme must be non-hierarchical, meaning no trailing slashes. const val APP_SCHEME = "example-app:" override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { return if (url?.startsWith(APP_SCHEME) == true) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length), "UTF-8") respondToData(urlData) true } else { false } }
Java
// The URL scheme must be non-hierarchical, meaning no trailing slashes. private static final String APP_SCHEME = "example-app:"; @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith(APP_SCHEME)) { urlData = URLDecoder.decode(url.substring(APP_SCHEME.length()), "UTF-8"); respondToData(urlData); return true; } return false; }
shouldOverrideUrlLoading()
API 主要用於啟動特定網址的意圖。實作時,請務必針對 WebView
處理的網址傳回 false
。但並非僅限於啟動意圖。您可以將啟動意圖替換為上述程式碼範例中的任何自訂行為。
瀏覽網頁記錄
WebView
覆寫網址載入作業時,會自動累積已造訪網頁的記錄。您可以使用 goBack()
和 goForward()
返回或往前瀏覽記錄。
例如,以下展示 Activity
如何使用裝置返回按鈕返回上一個畫面:
Kotlin
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { // Check whether the key event is the Back button and if there's history. if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) { myWebView.goBack() return true } // If it isn't the Back button or there isn't web page history, bubble up to // the default system behavior. Probably exit the activity. return super.onKeyDown(keyCode, event) }
Java
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { // Check whether the key event is the Back button and if there's history. if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { myWebView.goBack(); return true; } // If it isn't the Back button or there's no web page history, bubble up to // the default system behavior. Probably exit the activity. return super.onKeyDown(keyCode, event); }
如果應用程式使用 AndroidX AppCompat
1.6.0 以上版本,您可以進一步簡化先前的程式碼片段:
Kotlin
onBackPressedDispatcher.addCallback { // Check whether there's history. if (myWebView.canGoBack()) { myWebView.goBack() } }
Java
onBackPressedDispatcher.addCallback { // Check whether there's history. if (myWebView.canGoBack()) { myWebView.goBack(); } }
如果使用者可造訪的網頁歷史記錄,canGoBack()
方法會傳回 true。同樣地,您可以使用 canGoForward()
檢查是否有正向記錄。如果您未執行這項檢查,則在使用者到達記錄結束時,goBack()
和 goForward()
就不會有任何作用。
處理裝置設定變更
在執行階段期間,當裝置的設定有所變更,例如使用者旋轉裝置或關閉輸入法編輯器 (IME) 時,活動狀態就會變更。這些變更會導致系統刪除 WebView
物件的活動,並建立新的活動,同時建立一個新的 WebView
物件,用於載入已刪除物件的網址。如要修改活動的預設行為,您可以在資訊清單中變更其處理 orientation
變更的方式。如要進一步瞭解如何在執行階段期間處理設定變更,請參閱「處理設定變更」。
管理視窗
根據預設,系統會忽略開啟新視窗的要求。無論透過 JavaScript 或連結中的目標屬性開啟,皆是如此。您可以自訂 WebChromeClient
以提供自己的開啟多個視窗行為。
為了提升應用程式的安全性,建議您避免開啟彈出式視窗和新視窗。最安全的實作方式是將 "true"
傳遞至 setSupportMultipleWindows()
,但不要覆寫 setSupportMultipleWindows()
依附的 onCreateWindow()
方法。這個邏輯會禁止在連結中使用 target="_blank"
的任何頁面載入,