Compose使用指南-WebView最优雅的用法

4,085 阅读4分钟

Compose 中如何正确使用 WebView

Jetpack Compose 作为现代 Android 开发的 UI 框架,专注于构建声明式界面。然而,在某些场景下,你可能仍然需要使用 WebView 来展示网页内容。Jetpack Compose 没有内置 WebView 组件,但可以通过 AndroidView 来集成传统的 Android 视图系统,并在 Compose 中实现 WebView 的功能。

本文将详细介绍如何在 Jetpack Compose 中正确使用 WebView,包含基础用法、如何处理 WebView 的回调,以及封装通用的 WebView 组件。

一、Compose 中集成 WebView 的基础用法

Jetpack Compose 提供了 AndroidView 来嵌入传统的 Android 视图组件。通过这个接口,我们可以将 WebView 轻松集成到 Compose 的界面中。

1.1 使用 AndroidView 嵌入 WebView

我们首先来看一个简单的例子,如何在 Compose 中加载并显示网页内容:

@Composable
fun SimpleWebView() {
    AndroidView(factory = { context ->
        WebView(context).apply {
            // 启用 JavaScript
            settings.javaScriptEnabled = true
            // 加载网页
            loadUrl("https://www.example.com")
        }
    }, update = { webView ->
        // 可以在这里处理更新逻辑,例如重新加载页面等
    })
}

在这个例子中,AndroidViewfactory 参数允许我们在 Compose 中创建一个 WebView,并在其 apply 块中设置它的属性。通过 loadUrl 方法,我们可以加载一个网页。

1.2 启用 WebView 常见功能

WebView 通常需要一些额外的配置,像 JavaScript 支持、处理网页的导航返回等。让我们在 WebView 基础使用的基础上进一步完善配置:

@Composable
fun ConfiguredWebView(url: String) {
    AndroidView(factory = { context ->
        WebView(context).apply {
            settings.javaScriptEnabled = true
            settings.domStorageEnabled = true  // 启用 DOM 存储
            settings.loadWithOverviewMode = true  // 适应屏幕大小
            settings.useWideViewPort = true  // 启用广泛视图模式

            // 设置 WebViewClient 以防止外部浏览器打开链接
            webViewClient = WebViewClient()

            // 加载 URL
            loadUrl(url)
        }
    })
}

在这个例子中,我们增加了对 JavaScript 和 DOM 存储的支持,并通过 WebViewClient 来控制网页的加载行为,使其在 WebView 内部打开 URL 而不是跳转到外部浏览器。

二、处理 WebView 回调

WebView 提供了 WebViewClientWebChromeClient 用于处理网页加载的状态、JavaScript 回调、页面加载进度等事件。在 Compose 中,处理这些回调仍然非常简单,可以通过 update 参数传递不同的回调逻辑。

2.1 处理页面加载状态

通过 WebViewClient,我们可以监听页面加载的状态,例如开始加载、加载结束等:

@Composable
fun WebViewWithClient(url: String, onPageStarted: () -> Unit, onPageFinished: () -> Unit) {
    AndroidView(factory = { context ->
        WebView(context).apply {
            settings.javaScriptEnabled = true

            webViewClient = object : WebViewClient() {
                override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                    super.onPageStarted(view, url, favicon)
                    onPageStarted()
                }

                override fun onPageFinished(view: WebView?, url: String?) {
                    super.onPageFinished(view, url)
                    onPageFinished()
                }
            }

            loadUrl(url)
        }
    })
}

在这个示例中,我们通过 onPageStartedonPageFinished 回调来响应页面加载状态,可以在这两个回调中控制加载指示器的显示与隐藏。

2.2 支持 JavaScript 弹窗(Alert、Prompt、Confirm)

如果你希望 WebView 支持 JavaScript 弹窗(如 alert()prompt()confirm()),你需要配置 WebChromeClient。这也是很多 WebView 使用场景中不可或缺的一部分。

@Composable
fun WebViewWithJavaScriptDialogs(url: String) {
    AndroidView(factory = { context ->
        WebView(context).apply {
            settings.javaScriptEnabled = true

            // 设置 WebViewClient
            webViewClient = WebViewClient()

            // 配置 WebChromeClient 以支持 JavaScript 弹窗
            webChromeClient = object : WebChromeClient() {
                override fun onJsAlert(
                    view: WebView?, url: String?, message: String?, result: JsResult?
                ): Boolean {
                    AlertDialog.Builder(context)
                        .setMessage(message)
                        .setPositiveButton("确定") { _, _ -> result?.confirm() }
                        .show()
                    return true
                }

                override fun onJsConfirm(
                    view: WebView?, url: String?, message: String?, result: JsResult?
                ): Boolean {
                    AlertDialog.Builder(context)
                        .setMessage(message)
                        .setPositiveButton("确定") { _, _ -> result?.confirm() }
                        .setNegativeButton("取消") { _, _ -> result?.cancel() }
                        .show()
                    return true
                }
            }

            loadUrl(url)
        }
    })
}

此时,当网页触发 JavaScript 弹窗时,Compose 会使用 AlertDialog 来展示这些弹窗。

三、封装一个通用的 WebView 组件

为了方便在项目中重复使用,我们可以封装一个通用的 WebView 组件,支持传入 URL、JavaScript 配置以及加载进度回调等参数。

3.1 通用 WebView 组件

我们可以封装一个 ComposeWebView,支持设置 URL 和一些常用的配置:

@Composable
fun ComposeWebView(
    url: String,
    modifier: Modifier = Modifier,
    javaScriptEnabled: Boolean = true,
    domStorageEnabled: Boolean = true,
    onPageStarted: (() -> Unit)? = null,
    onPageFinished: (() -> Unit)? = null
) {
    AndroidView(
        factory = { context ->
            WebView(context).apply {
                settings.javaScriptEnabled = javaScriptEnabled
                settings.domStorageEnabled = domStorageEnabled

                webViewClient = object : WebViewClient() {
                    override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                        super.onPageStarted(view, url, favicon)
                        onPageStarted?.invoke()
                    }

                    override fun onPageFinished(view: WebView?, url: String?) {
                        super.onPageFinished(view, url)
                        onPageFinished?.invoke()
                    }
                }

                loadUrl(url)
            }
        },
        modifier = modifier
    )
}

3.2 如何使用通用 WebView 组件

封装后的 ComposeWebView 可以直接在项目中使用,传入 URL 和所需的配置参数:

@Composable
fun WebViewExample() {
    var isLoading by remember { mutableStateOf(true) }

    Column(modifier = Modifier.fillMaxSize()) {
        if (isLoading) {
            CircularProgressIndicator(modifier = Modifier.align(Alignment.CenterHorizontally))
        }

        ComposeWebView(
            url = "https://www.example.com",
            onPageStarted = { isLoading = true },
            onPageFinished = { isLoading = false }
        )
    }
}

在这个示例中,我们通过 onPageStartedonPageFinished 来控制加载状态,页面加载完成时隐藏加载指示器。

四、总结

Jetpack Compose 虽然是一个全新的声明式 UI 框架,但仍然可以通过 AndroidView 轻松集成传统的 Android 视图,如 WebView。我们可以通过简单的方式实现 WebView 的基础功能,并进一步扩展它的功能,比如处理 JavaScript 回调、页面加载状态等。通过封装通用的 WebView 组件,可以极大地提高开发效率和代码复用性。

希望本文能帮助你更好地理解如何在 Jetpack Compose 中正确使用 WebView,并为你的项目带来帮助。如果你有任何问题或建议,欢迎在评论区留言讨论。