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

如何在Jetpack Compose中嵌入Activity而非启动外部Activity?

无法直接将Activity嵌入Jetpack Compose组件中

Activity是Android的顶层独立组件,拥有专属的窗口生命周期,没法直接嵌入到Composable内部显示。你当前的代码是启动了一个全新的独立Activity,所以只会打开新页面而非嵌入效果。

正确的做法是把原Activity中的WebView逻辑迁移到Composable里,用AndroidView组件承载原生WebView,这样就能在Composable中显示WebView内容,同时保留原有的文件选择等功能。

实现步骤

1. 封装WebView为Composable组件

用AndroidView创建WebView实例,复用你原有的WebView配置、WebViewClient和WebChromeClient逻辑,同时通过Activity Result API处理文件选择回调:

@Composable
fun WebViewComposable(url: String = "http://example.com") {
    val context = LocalContext.current
    // 处理文件选择的Activity Result回调
    val filePickerLauncher = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        val webView = (context as? ComponentActivity)?.findViewById<WebView>(R.id.web_view_in_compose)
        webView?.let {
            when (result.resultCode) {
                Activity.RESULT_OK -> {
                    result.data?.let { intent ->
                        // 适配不同Android版本的文件选择结果
                        val chromeClient = it.webChromeClient as? CustomWebChromeClient
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            chromeClient?.uploadMessage?.onReceiveValue(
                                WebChromeClient.FileChooserParams.parseResult(
                                    result.resultCode,
                                    intent
                                )
                            )
                        } else {
                            chromeClient?.mUploadMessage?.onReceiveValue(intent.data)
                        }
                    }
                }
                Activity.RESULT_CANCELED -> {
                    // 取消选择时清空回调避免内存泄漏
                    val chromeClient = it.webChromeClient as? CustomWebChromeClient
                    chromeClient?.uploadMessage?.onReceiveValue(null)
                    chromeClient?.mUploadMessage?.onReceiveValue(null)
                }
            }
        }
    }

    AndroidView(
        factory = { ctx ->
            WebView(ctx).apply {
                id = R.id.web_view_in_compose // 设置ID方便后续查找
                settings.javaScriptEnabled = true
                webViewClient = XWebViewClient()
                webChromeClient = CustomWebChromeClient { intent, _ ->
                    // 启动文件选择器
                    filePickerLauncher.launch(intent)
                }
                loadUrl(url)
            }
        },
        update = { webView ->
            // 当URL变化时更新WebView内容
            if (webView.url != url) {
                webView.loadUrl(url)
            }
        },
        onDispose = { webView ->
            // 组件销毁时清理WebView
            webView.stopLoading()
            webView.destroy()
        }
    )
}

// 自定义WebChromeClient,封装文件选择逻辑
class CustomWebChromeClient(
    private val onLaunchFilePicker: (Intent, Int) -> Unit
) : WebChromeClient() {
    var mUploadMessage: ValueCallback<Uri>? = null
    var uploadMessage: ValueCallback<Array<Uri>>? = null
    private val REQUEST_SELECT_FILE = 100
    private val FILECHOOSER_RESULTCODE = 1

    override fun onShowFileChooser(
        webView: WebView?,
        filePathCallback: ValueCallback<Array<Uri>>?,
        fileChooserParams: FileChooserParams
    ): Boolean {
        uploadMessage?.onReceiveValue(null)
        uploadMessage = filePathCallback

        val intent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            fileChooserParams.createIntent()
        } else {
            Intent(Intent.ACTION_GET_CONTENT).apply {
                addCategory(Intent.CATEGORY_OPENABLE)
                type = "image/*"
            }
        }
        onLaunchFilePicker(Intent.createChooser(intent, "File Browser"), REQUEST_SELECT_FILE)
        return true
    }

    // 兼容低版本Android的文件选择方法
    @Suppress("DEPRECATION")
    fun openFileChooser(uploadMsg: ValueCallback<Uri>?, acceptType: String?) {
        mUploadMessage = uploadMsg
        val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
            addCategory(Intent.CATEGORY_OPENABLE)
            type = "image/*"
        }
        onLaunchFilePicker(Intent.createChooser(intent, "File Browser"), FILECHOOSER_RESULTCODE)
    }

    @Suppress("DEPRECATION")
    fun openFileChooser(uploadMsg: ValueCallback<Uri>?, acceptType: String?, capture: String?) {
        openFileChooser(uploadMsg, acceptType)
    }

    @Suppress("DEPRECATION")
    fun openFileChooser(uploadMsg: ValueCallback<Uri>?) {
        openFileChooser(uploadMsg, "image/*")
    }
}

// 复用原有的WebViewClient逻辑
private class XWebViewClient : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView, url: String?): Boolean {
        url?.let { view.loadUrl(it) }
        return true
    }
}

2. 在你的Composable中使用WebView组件

直接调用封装好的组件即可,无需启动独立Activity:

@Composable
fun MyContent() {
    WebViewComposable(url = "http://example.com")
}

3. 必要权限配置

确保在AndroidManifest.xml中添加网络权限:

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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 21:30:43