在 WebView 中建構網頁應用程式

使用 WebView 將網頁應用程式或網頁當做用戶端應用程式的一部分提供。WebView 類別是 Android 的 View 類別延伸項目,可讓您在活動版面配置中顯示網頁。但不包含完整網路瀏覽器的功能,例如導覽控制項或網址列。根據預設,所有的 WebView 都會顯示網頁。

WebView 可協助您在應用程式中提供可能需要更新的資訊,例如使用者協議或使用者指南。您可以在 Android 應用程式中建立包含 WebViewActivity,然後使用該檔案顯示線上代管的文件。

當應用程式向使用者提供需要透過網路連線擷取資料 (例如電子郵件) 的資料時,WebView 也能提供協助。在這種情況下,您可能會發現,在 Android 應用程式中建構 WebView 會比較容易,因為您可以直接顯示含有所有使用者資料的網頁,而不需要執行網路要求,然後剖析資料並在 Android 版面配置中算繪。您可以改為設計專為 Android 裝置打造的網頁,然後在 Android 應用程式中導入 WebView,以便載入網頁。

這份文件說明如何開始使用 WebView,以及如何進行繫結 從網頁使用 JavaScript 到 Android 應用程式的用戶端程式碼;如何 處理網頁導覽,以及如何在使用 WebView 時管理視窗。

在舊版 Android 上使用 WebView

為了安全地在您的應用程式中使用較新的 WebView 功能 執行時,請將 AndroidX Webkit 程式庫。這是靜態的 您可以在應用程式中加入程式庫,android.webkit使用非支援的 API 適用於早期平台版本

請按照下列步驟將這個檔案新增至 build.gradle 檔案:

KotlinGroovy
dependencies {
    implementation("androidx.webkit:webkit:1.8.0")
}
dependencies {
    implementation ("androidx.webkit:webkit:1.8.0")
}

探索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(),你好: 如以下範例所示:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.loadUrl("http://www.example.com")
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");

在 onCreate() 中新增 WebView

如要改為在活動的 onCreate() 方法中將 WebView 新增至應用程式,請使用 類似以下的邏輯:

KotlinJava
val myWebView = WebView(activityContext)
setContentView(myWebView)
WebView myWebView = new WebView(activityContext);
setContentView(myWebView);

然後載入頁面:

KotlinJava
myWebView.loadUrl("http://www.example.com")
myWebView.loadUrl("https://www.example.com");

或者,您也可以從 HTML 字串載入網址:

KotlinJava
// 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")
// 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 對話方塊傳送至 內容。如要進一步瞭解在這個情況下如何偵錯,請參閱「偵錯網頁應用程式」。
  • 處理影響內容轉譯的事件,例如表單錯誤 透過 YAML 檔案 WebViewClient。您也可以使用這個子類別來攔截網址載入作業。
  • 修改 WebSettings 啟用 JavaScript。
  • 使用 JavaScript 存取您已插入的 Android 架構物件 放入 WebView 中。

在 WebView 中使用 JavaScript

如要透過 WebView 載入的網頁使用 JavaScript,您必須 為 WebView 啟用 JavaScript。啟用 JavaScript 後 在應用程式程式碼和 JavaScript 程式碼之間建立介面。

啟用 JavaScript

根據預設,WebView 會停用 JavaScript。您可以透過附加至 WebViewWebSettings 啟用這項功能。使用以下方式擷取 WebSettingsgetSettings(),然後啟用 JavaScript 值為 setJavaScriptEnabled()

請參閱以下範例:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.settings.javaScriptEnabled = true
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);

WebSettings 可讓您存取其他各種設定 很實用例如,假設您要開發 專為 Android 應用程式中的 WebView 所設計,因此您可以定義自訂 使用者代理程式字串 setUserAgentString(), 接著查詢您網頁中的自訂使用者代理程式,驗證用戶端 也就是 Android 應用程式

將 JavaScript 程式碼繫結至 Android 程式碼

開發專為 WebView 設計的網頁應用程式時 在 Android 應用程式中,您可以建立 JavaScript 程式碼和 用戶端 Android 程式碼舉例來說,JavaScript 程式碼可以呼叫 Android 程式碼中的某個方法,以便顯示 Dialog,而非使用 JavaScript 的 alert() 函數。

如要在 JavaScript 和 Android 程式碼之間繫結新版介面,請呼叫 addJavascriptInterface(), 傳送類別實例,以便繫結至您的 JavaScript 和介面名稱 讓 JavaScript 可以呼叫來存取類別。

舉例來說,您可以在 Android 應用程式中加入下列類別:

KotlinJava
/** 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()
    }
}
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,如以下範例所示:

KotlinJava
val webView: WebView = findViewById(R.id.webview)
webView.addJavascriptInterface(WebAppInterface(this), "Android")
WebView webView = (WebView) findViewById(R.id.webview);
webView.addJavascriptInterface(new WebAppInterface(this), "Android");

這會建立名為 Android 的介面,供在 WebView。此時,您的網頁應用程式可存取 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

如要開啟使用者輕觸的連結,請提供 WebViewWebViewClient 使用 setWebViewClient()。 使用者點選的所有連結都會在您的 WebView 中載入。如要進一步控管點選連結的載入位置,請自行建立 WebViewClient 來覆寫 shouldOverrideUrlLoading() 方法。以下範例假設 MyWebViewClient 是內部類別 (共 Activity 個)。

KotlinJava
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
    }
}
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 的例項:

KotlinJava
val myWebView: WebView = findViewById(R.id.webview)
myWebView.webViewClient = MyWebViewClient()
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() 方法中處理這個網址,例如 :

KotlinJava
// 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
    }
}
// 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 如何使用裝置的背面 按鈕前往上一頁:

KotlinJava
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)
}
@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 以上版本,您可以進一步簡化上述程式碼片段:

KotlinJava
onBackPressedDispatcher.addCallback {
    // Check whether there's history.
    if (myWebView.canGoBack()) {
        myWebView.goBack()
    }
}
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" 的網頁載入。