Kotlin 接入webview

在 Kotlin 中接入 WebView(通常是在 Android 应用中)是一个常见的需求。

1. 基础集成

添加网络权限

首先,在 AndroidManifest.xml 中添加网络权限:

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

布局文件中添加 WebView

在 XML 布局文件中添加 WebView 组件:

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Kotlin 代码中的基本配置

// MainActivity.kt
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    
    private lateinit var webView: WebView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        webView = findViewById(R.id.webView)
        
        // 基础配置
        setupWebView()
        
        // 加载网页
        webView.loadUrl("https://www.example.com")
    }
    
    private fun setupWebView() {
        // 启用 JavaScript
        webView.settings.javaScriptEnabled = true
        
        // 启用 DOM 存储
        webView.settings.domStorageEnabled = true
        
        // 设置 WebViewClient 以确保链接在 WebView 内打开
        webView.webViewClient = WebViewClient()
        
        // 可选:启用缩放控制
        webView.settings.setSupportZoom(true)
        webView.settings.builtInZoomControls = true
        webView.settings.displayZoomControls = false
    }
}

2. 高级配置和功能

自定义 WebViewClient 处理页面加载

private fun setupWebView() {
    webView.webViewClient = object : WebViewClient() {
        // 在页面开始加载时调用
        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            super.onPageStarted(view, url, favicon)
            // 显示加载进度条
            showLoadingProgress()
        }
        
        // 在页面加载完成时调用
        override fun onPageFinished(view: WebView?, url: String?) {
            super.onPageFinished(view, url)
            // 隐藏加载进度条
            hideLoadingProgress()
        }
        
        // 处理 URL 重定向(API 24+)
        override fun shouldOverrideUrlLoading(
            view: WebView?, 
            request: WebResourceRequest?
        ): Boolean {
            request?.url?.let { url ->
                // 在这里可以拦截特定 URL 进行处理
                if (url.toString().startsWith("https://special-link.com")) {
                    // 处理特定链接,比如打开原生页面
                    handleSpecialLink(url.toString())
                    return true // 阻止 WebView 加载该 URL
                }
            }
            return false // 允许 WebView 加载该 URL
        }
        
        // 兼容旧版本的处理方法
        override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
            url?.let {
                if (it.startsWith("https://special-link.com")) {
                    handleSpecialLink(it)
                    return true
                }
            }
            return false
        }
        
        // 处理加载错误
        override fun onReceivedError(
            view: WebView?,
            request: WebResourceRequest?,
            error: WebResourceError?
        ) {
            super.onReceivedError(view, request, error)
            // 显示错误页面
            showErrorPage()
        }
    }
}

配置 WebChromeClient 处理进度和对话框

private fun setupWebView() {
    webView.webChromeClient = object : WebChromeClient() {
        // 显示加载进度
        override fun onProgressChanged(view: WebView?, newProgress: Int) {
            super.onProgressChanged(view, newProgress)
            updateProgressBar(newProgress)
        }
        
        // 处理 JavaScript 的 alert 对话框
        override fun onJsAlert(
            view: WebView?, 
            url: String?, 
            message: String?, 
            result: JsResult?
        ): Boolean {
            AlertDialog.Builder(this@MainActivity)
                .setMessage(message)
                .setPositiveButton("OK") { _, _ -> result?.confirm() }
                .setCancelable(false)
                .create()
                .show()
            return true
        }
        
        // 处理 JavaScript 的确认对话框
        override fun onJsConfirm(
            view: WebView?, 
            url: String?, 
            message: String?, 
            result: JsResult?
        ): Boolean {
            AlertDialog.Builder(this@MainActivity)
                .setMessage(message)
                .setPositiveButton("OK") { _, _ -> result?.confirm() }
                .setNegativeButton("Cancel") { _, _ -> result?.cancel() }
                .setCancelable(false)
                .create()
                .show()
            return true
        }
    }
}

3. Kotlin 与 JavaScript 交互

从 Kotlin 调用 JavaScript 函数

// 调用无参 JavaScript 函数
fun callJavaScriptFunction() {
    webView.evaluateJavascript("javascript:yourJavaScriptFunction()") { result ->
        // 处理返回结果
        println("JavaScript 返回: $result")
    }
}

// 调用带参 JavaScript 函数
fun callJavaScriptWithParams() {
    val param = "Hello from Kotlin"
    webView.evaluateJavascript("javascript:yourFunction('$param')") { result ->
        println("结果: $result")
    }
}

// 加载页面后调用
webView.webViewClient = object : WebViewClient() {
    override fun onPageFinished(view: WebView?, url: String?) {
        super.onPageFinished(view, url)
        // 页面加载完成后调用 JavaScript 函数
        callJavaScriptFunction()
    }
}

从 JavaScript 调用 Kotlin 函数

// 创建 JavaScript 接口类
class WebAppInterface(private val context: Context) {
    
    // 使用 @JavascriptInterface 注解暴露给 JavaScript
    @JavascriptInterface
    fun showToast(message: String) {
        Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
    }
    
    @JavascriptInterface
    fun getUserData(): String {
        return "用户数据来自 Kotlin"
    }
    
    @JavascriptInterface
    fun navigateToNativeScreen(screenName: String) {
        // 处理从 JavaScript 发来的导航请求
        when (screenName) {
            "profile" -> startActivity(Intent(context, ProfileActivity::class.java))
            "settings" -> startActivity(Intent(context, SettingsActivity::class.java))
        }
    }
}

// 在 WebView 设置中添加接口
private fun setupWebView() {
    // 添加 JavaScript 接口
    webView.addJavascriptInterface(WebAppInterface(this), "Android")
    
    // 在 HTML/JavaScript 中可以通过 window.Android 调用这些方法
    // 例如:window.Android.showToast("Hello from Web!");
}

JavaScript 端的对应代码

<!DOCTYPE html>
<html>
<head>
    <title>WebView 示例</title>
</head>
<body>
    <h1>WebView 与 Kotlin 交互示例</h1>
    
    <button onclick="callKotlinFunction()">调用 Kotlin 函数</button>
    <button onclick="getDataFromKotlin()">从 Kotlin 获取数据</button>

    <script>
        // 从 JavaScript 调用 Kotlin 函数
        function callKotlinFunction() {
            if (window.Android) {
                window.Android.showToast("Hello from JavaScript!");
                window.Android.navigateToNativeScreen("profile");
            }
        }
        
        // 从 Kotlin 获取数据
        function getDataFromKotlin() {
            if (window.Android) {
                const data = window.Android.getUserData();
                alert("从 Kotlin 接收的数据: " + data);
            }
        }
        
        // 供 Kotlin 调用的函数
        function yourJavaScriptFunction() {
            return "这是来自 JavaScript 的响应";
        }
        
        function yourFunction(param) {
            console.log("收到 Kotlin 参数: " + param);
            return "处理完成: " + param;
        }
    </script>
</body>
</html>

4. 处理返回键导航

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // 检查是否是返回键并且 WebView 可以返回
    if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
        webView.goBack() // 在 WebView 内返回上一页
        return true
    }
    // 否则执行默认的返回行为(退出 Activity)
    return super.onKeyDown(keyCode, event)
}

5. 安全注意事项

安全的 JavaScript 接口实现

class SafeWebAppInterface(private val context: Context) {
    
    @JavascriptInterface
    fun processUserInput(input: String) {
        // 始终在主线程执行
        Handler(Looper.getMainLooper()).post {
            // 验证和清理输入
            val safeInput = input.trim().take(100) // 限制输入长度
            
            // 执行 UI 操作
            Toast.makeText(context, "安全处理: $safeInput", Toast.LENGTH_SHORT).show()
        }
    }
}

安全的 WebView 配置

private fun setupSecureWebView() {
    val webSettings = webView.settings
    
    // 启用必要的设置
    webSettings.javaScriptEnabled = true
    webSettings.domStorageEnabled = true
    
    // 安全设置 - 根据需求调整
    webSettings.allowFileAccess = false
    webSettings.allowContentAccess = false
    webSettings.allowFileAccessFromFileURLs = false
    webSettings.allowUniversalAccessFromFileURLs = false
    
    // 防止通过文件 scheme 加载敏感文件
    webView.webViewClient = object : WebViewClient() {
        override fun shouldInterceptRequest(
            view: WebView?,
            request: WebResourceRequest?
        ): WebResourceResponse? {
            request?.url?.let { url ->
                if (url.scheme == "file" && url.path?.contains("/data/") == true) {
                    // 阻止访问敏感文件路径
                    return WebResourceResponse(null, null, null)
                }
            }
            return super.shouldInterceptRequest(view, request)
        }
    }
}

6. 加载本地 HTML 文件

// 加载 assets 文件夹中的 HTML 文件
fun loadLocalHtml() {
    webView.loadUrl("file:///android_asset/local_page.html")
}

// 或者加载 raw 资源
fun loadRawHtml() {
    val htmlContent = resources.openRawResource(R.raw.local_page)
        .bufferedReader().use { it.readText() }
    webView.loadDataWithBaseURL(
        null, 
        htmlContent, 
        "text/html", 
        "UTF-8", 
        null
    )
}

完整示例

class MainActivity : AppCompatActivity() {
    
    private lateinit var webView: WebView
    private lateinit var progressBar: ProgressBar
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        webView = findViewById(R.id.webView)
        progressBar = findViewById(R.id.progressBar)
        
        setupWebView()
        webView.loadUrl("https://www.example.com")
    }
    
    private fun setupWebView() {
        val webSettings = webView.settings
        
        // 基础设置
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        webSettings.setSupportZoom(true)
        
        // 添加 JavaScript 接口
        webView.addJavascriptInterface(WebAppInterface(this), "Android")
        
        // 设置 WebViewClient
        webView.webViewClient = object : WebViewClient() {
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                progressBar.visibility = View.VISIBLE
            }
            
            override fun onPageFinished(view: WebView?, url: String?) {
                progressBar.visibility = View.GONE
            }
        }
        
        // 设置 WebChromeClient 处理进度
        webView.webChromeClient = object : WebChromeClient() {
            override fun onProgressChanged(view: WebView?, newProgress: Int) {
                progressBar.progress = newProgress
                if (newProgress == 100) {
                    progressBar.visibility = View.GONE
                }
            }
        }
    }
    
    override fun onBackPressed() {
        if (webView.canGoBack()) {
            webView.goBack()
        } else {
            super.onBackPressed()
        }
    }
}

这些示例涵盖了 Kotlin 中接入 WebView 的主要方面,你可以根据具体需求进行调整和扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值