くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

AndroidでWebViewを使ってみる

今回はAndroidのWebViewについてです

iOSと違う部分もあるので見ていきましょう!

Webページを表示させる

Webページを表示させるだけであれば簡単にできます

前提としてAndroidManifest.xmlでインターネットの権限の設定を行ってください

<manifest ... >
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

レイアウトファイルでwebviewを作成して、表示させるURLを読み込む処理を行うのみです

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
webView.loadUrl("http://www.example.com")

表示させるのはこれだけでできます。ここからは細かい設定について説明します

WebViewの設定を追加する

WebViewの設定はWebSettingsを利用します。設定は次のように行います(一部抜粋します)

// JavaScriptを有効にする(ここでのwebViewはレイアウトのID)
webView.settings.javaScriptEnabled = true

// ピンチ操作によるズームイン・ズームアウトの有効化
webView.settings.builtInZoomControls = true

// ダブルタップによるズームイン・ズームアウトの有効化
webView.settings.useWideViewPort = true

// ズームボタン(右下に表示される±のルーペ)の表示・非表示切り替え
webView.settings.displayZoomControls = true

// // Storageの使用許可の設定
webView.settings.domStorageEnabled = true

// trueの場合にはHTMLのwidthがWebViewより大きい時に全体を表示するようによしなに調整される
webView.settings.loadWithOverviewMode = true

// ズーム機能をサポートするか否か
webView.settings.setSupportZoom(true)

webViewClientの設定

webViewClientはWebView内のセキュリティやルーティングなどのほとんどのアクションを担当するオブジェクトです

webViewClientの主な処理は次です

webView.webViewClient = object :WebViewClient() {

      // WebView内の別のリンクを表示させる際に
      override fun shouldOverrideUrlLoading(
          view: WebView?,
          request: WebResourceRequest?
      ): Boolean {
          // shouldOverrideUrlLoadingから別のActivityやアプリを起動する場合はtrueを返す。falseを返すとWebView内にロードした結果を表示
          return false
      }

      // ページ読み込み開始時
      override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
          super.onPageStarted(view, url, favicon)
      }

      // ページ読み込み完了時
      override fun onPageFinished(view: WebView?, url: String?) {
          super.onPageFinished(view, url)
      }

      // アクセスエラーなどのWebリソースの読み込みエラー発生時の処理
      override fun onReceivedError(
          view: WebView?,
          request: WebResourceRequest?,
          error: WebResourceError?
      ) {
          super.onReceivedError(view, request, error)

          // ここを追加しないと画像などが表示できない場合もエラー扱いになる
          if (request.isForMainFrame) {
          }
      }

      // SSLエラー時
      override fun onReceivedSslError(
          view: WebView?,
          handler: SslErrorHandler?,
          error: SslError?
      ) {
          super.onReceivedSslError(view, handler, error)

          handler?.cancel()
    }
}

注意点としてはonReceivedErrorを受け取ってもonPageFinishedは実行されるので、画面の制御などに気をつけましょう

HTMLを表示させる

次にHTMLを表示させる方法です

HTMLはassetsフォルダを作成しておきます

assetsフォルダ

HTMLファイルを置いたら、呼び出してロードするだけで表示できます

// 今回の場合にはtest.htmlというファイルを格納している想定です
webView.loadUrl("file:///android_asset/test.html")

CSSを適用させるにはページ読み込み完了したらCSSファイルを読み込む処理を行います

CSSはHTMLと同様にassetsフォルダに置いているという条件付きです。またonPageFinishedで行います

private fun injectCSS() {
            try {
                val inputStream = assets.open("style.css")   // CSSファイル名を指定
                val buffer = ByteArray(inputStream.available())
                inputStream.read(buffer)
                inputStream.close()
                val encoded = Base64.encodeToString(buffer , Base64.NO_WRAP)
                webframe.loadUrl(
                    "javascript:(function() {" +
                            "var parent = document.getElementsByTagName('head').item(0);" +
                            "var style = document.createElement('style');" +
                            "style.type = 'text/css';" +
                            // Tell the browser to BASE64-decode the string into your script !!!
                            "style.innerHTML = window.atob('" + encoded + "');" +
                            "parent.appendChild(style)" +
                            "})()"
                )
            } catch (e: Exception) {
                e.printStackTrace()
            }

        }

Javascriptとの連携

Javascriptからアプリの処理を実行したり、アプリからJavaScriptのメソッドを実行したい場合があると思います

Javascriptとの連携はWebChromeClientを使います

WebChromeClientはブラウザー周辺のchromeの要素を変更するイベントに反応するためのイベントインターフェイスです。 JavaScript、favicons、進行状況と現在のページのタイトルの更新などが含まれます

Javascriptからアプリの処理を実行する

例えば、Javascriptでボタンのタップアクションをアプリ側で受け取りアプリで処理を行う場合です

HTMLは次の例を使用します

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <script type="text/javascript">
            function showMessage() {
                alert("showMwssage");
            }
            function setMessage(message) {
                document.getElementById("text").innerHTML = message;
            }
        </script>
        <input type="button" value="Button"
               onClick="showMessage();" />
        <p id="text"></p>
    </body>
</html>

HTMLで設定されるfunction showMessage()でalertを設定して次のようにonJsAlertでメッセージを取得できます

webView.webChromeClient = object :WebChromeClient() {
    override fun onJsAlert(
       view: WebView?,
        url: String?,
        message: String?,
        result: JsResult?
    ): Boolean {
        if (!message.isNullOrEmpty()) {
            Log.d("jsAlertMessage", message)   // showMwssageが取得される
         }
        return true
    }
}

アラートメッセージを取得したら処理が行われるようにしたらJavascriptからアプリの処理を実行したことになります

アプリからJavaScriptのメソッドを実行する

今度は先程と逆でアプリからJavascriptを実行する方法です

次のようにevaluateJavascriptJavascriptの関数を呼び出します

val message = "TEST"
webView.evaluateJavascript("setMessage(`${message}`);")

注意点

オフラインなどでWebViewが表示できない時はデフォルトのエラー画面ではなく、エラーメッセージを表示させる・エラーイラストを表示させるなどの対応を行うのが望ましいです

そうしないとリリース時にNGを受けることがあります

iOSではNGを受けないかもしれないですが、同じ対応を行うのが望ましいです

参照

android-tech.jp

developer.android.com

qiita.com

qiita.com

https://android.camposha.info/ja/android-webview/#gsc.tab=0android.camposha.info

qiita.com

qiita.com