You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中从可编辑WebView获取内容?

如何从ViewModel获取Compose可编辑WebView的富文本内容

要实现从ViewModel获取Accompanist WebView的富文本内容,核心是通过JavaScript获取WebView内可编辑区域的HTML内容,再将内容传递给ViewModel。以下是具体实现步骤:


1. ViewModel中定义内容接收状态

首先在ViewModel里创建一个可观察的状态容器,用于接收和存储WebView的内容:

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

class EditorViewModel : ViewModel() {
    // 私有可变流,用于内部更新内容
    private val _webContent = MutableStateFlow("")
    // 公开只读流,供UI层观察
    val webContent = _webContent.asStateFlow()

    // 供UI层调用,更新ViewModel内的内容
    fun updateWebContent(content: String) {
        _webContent.value = content
    }
}

2. 保存WebView实例并触发内容获取

在Compose代码中,先保存WebView的实例,然后通过按钮(或其他触发时机)调用JS获取内容,处理后传递给ViewModel:

关键修改点:

  • 用mutableStateOf保存WebView实例,方便后续调用evaluateJavascript
  • 调用JS获取可编辑区域的innerHTML,清理转义字符串后更新ViewModel
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsStateWithLifecycle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewStateWithHTMLData
import android.webkit.WebView as AndroidWebView

@Composable
fun EditorScreen() {
    val viewModel: EditorViewModel = viewModel()
    val webContent by viewModel.webContent.collectAsStateWithLifecycle()
    
    // 保存WebView实例,用于后续调用JS
    val webViewInstance = remember { mutableStateOf<AndroidWebView?>(null) }
    
    // 确保可编辑div有明确id(示例中为editor)
    val contentEditableHtml = "<div id=\"editor\" contenteditable=\"true\">"
    val messageContentHtml = "" // 你的初始内容
    val fullContentHtml = rememberWebViewStateWithHTMLData(
        data = "$contentEditableHtml$messageContentHtml</div>"
    )

    // 触发内容获取的按钮
    Button(
        onClick = {
            webViewInstance.value?.evaluateJavascript(
                // JS代码:获取可编辑区域的HTML内容
                "document.getElementById('editor').innerHTML;",
                // 回调处理返回的内容
                android.webkit.ValueCallback { htmlContent ->
                    // 清理JS返回的转义双引号
                    val cleanedContent = htmlContent?.removeSurrounding("\"") ?: ""
                    viewModel.updateWebContent(cleanedContent)
                }
            )
        }
    ) {
        Text("保存富文本内容")
    }

    // 你的WebView代码修改
    WebView(
        modifier = Modifier
            .fillMaxSize()
            .padding(horizontal = 8.dp),
        state = fullContentHtml,
        onCreated = { webView ->
            webView.settings.javaScriptEnabled = true
            webView.settings.allowContentAccess = true
            // 保存WebView实例
            webViewInstance.value = webView
        },
        client = remember { webViewClient },
        chromeClient = remember { chromeViewClient }
    )
}

3. 可选:实时监听内容变化

如果需要实时同步用户输入到ViewModel,可以在WebViewClient的onPageFinished中注入监听input事件的JS,并通过JavascriptInterface传递内容:

步骤3.1:自定义WebViewClient

import com.google.accompanist.web.AccompanistWebViewClient

class EditorWebViewClient : AccompanistWebViewClient() {
    override fun onPageFinished(view: AndroidWebView?, url: String?) {
        super.onPageFinished(view, url)
        // 注入你的原有CSS代码
        val cssInjectionJs = """
            // 你的CSS注入逻辑
        """.trimIndent()
        view?.evaluateJavascript(cssInjectionJs, null)

        // 注入内容变化监听JS
        val contentListenJs = """
            document.getElementById('editor').addEventListener('input', function() {
                // 调用Android端的回调方法
                window.editorCallback.onContentChanged(document.getElementById('editor').innerHTML);
            });
        """.trimIndent()
        view?.evaluateJavascript(contentListenJs, null)
    }
}

步骤3.2:添加JavascriptInterface关联ViewModel

在WebView的onCreated方法中添加JS接口,将内容变化直接回调到ViewModel:

onCreated = { webView ->
    webView.settings.javaScriptEnabled = true
    webView.settings.allowContentAccess = true
    webViewInstance.value = webView

    // 添加JS接口,接收内容变化回调
    webView.addJavascriptInterface(object {
        @android.webkit.JavascriptInterface
        fun onContentChanged(content: String) {
            viewModel.updateWebContent(content)
        }
    }, "editorCallback")
}

注意事项

  • 确保可编辑的HTML元素有唯一id,否则JS无法准确定位内容
  • evaluateJavascript返回的字符串会带有转义双引号,需用removeSurrounding("\"")清理
  • 实时监听可能频繁触发回调,可根据需求添加防抖逻辑

内容的提问来源于stack exchange,提问作者Slava

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 21:17:18