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

