如何在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
相关产品推荐
相关产品推荐

