使用 WebView
傳送網頁應用程式或網頁,做為用戶端應用程式的一部分。WebView
類別是 Android View
類別的擴充功能,可讓您在活動版面配置中顯示網頁。這類檢視畫面不包含完整開發的網路瀏覽器功能,例如導覽控制項或網址列。根據預設,WebView
只會顯示網頁。
WebView
可協助您在應用程式中提供可能需要更新的資訊,例如使用者合約或使用者指南。在 Android 應用程式中,您可以建立包含 WebView
的 Activity
,然後使用該 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
訊息。
您可以將這個類別繫結至 WebView
中執行的 JavaScript,方法是使用 addJavascriptInterface()
,如下列範例所示:
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");
這會為 WebView
中執行的 JavaScript 建立名為 Android
的介面。此時,您的網頁應用程式可以存取 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
前後瀏覽網頁記錄。
如要開啟使用者輕觸的連結,請為 WebView
提供 WebViewClient
,方法是使用 setWebViewClient()
。
使用者輕觸的所有連結都會在 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 主要用於啟動特定網址的 Intent。實作時,請務必針對 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"
的任何網頁。