- 支持选择本地文件
- 支持返回键后退到
- 支持http和https地址
添加权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
添加http链接支持
xml下创建network_security_config.xml文件
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
AndroidManifest.xml的application中引用network_security_config
android:networkSecurityConfig="@xml/network_security_config"
加载网页完整代码
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import com.cwj.test20251224.ui.theme.Test20251224Theme
import androidx.compose.runtime.Composable
import androidx.compose.ui.viewinterop.AndroidView
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.ui.Modifier
class MainActivity : ComponentActivity() {
private lateinit var filePickerLauncher: ActivityResultLauncher<Intent>
private var webView: WebView? = null
private var backPressedCallback: OnBackPressedCallback? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
filePickerLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
val resultArray = when {
result.resultCode == RESULT_OK && result.data != null -> {
WebChromeClient.FileChooserParams.parseResult(result.resultCode, result.data)
}
else -> null
}
FileChooserHelper.onActivityResult(resultArray)
}
setContent {
Test20251224Theme {
Scaffold(modifier = Modifier.fillMaxSize()) { _ ->
WebPageContainer(
url = "https://juejin.cn/",
filePickerLauncher = filePickerLauncher,
onWebViewCreated = { webView ->
this.webView = webView
registerBackPressedCallback()
}
)
}
}
}
}
// 注册返回按键处理
private fun registerBackPressedCallback() {
backPressedCallback?.remove()
backPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (webView?.canGoBack() == true) {
webView?.goBack()
} else {
isEnabled = false
onBackPressedDispatcher.onBackPressed()
}
}
}
onBackPressedDispatcher.addCallback(this, backPressedCallback!!)
}
}
// 文件选择处理
object FileChooserHelper {
private var filePathCallback: ValueCallback<Array<Uri>>? = null
fun setFilePathCallback(callback: ValueCallback<Array<Uri>>) {
filePathCallback?.onReceiveValue(null)
filePathCallback = callback
}
fun onActivityResult(uris: Array<Uri>?) {
filePathCallback?.let { callback ->
callback.onReceiveValue(uris ?: arrayOf())
filePathCallback = null
}
}
}
@Composable
fun WebPageContainer(
url: String,
filePickerLauncher: ActivityResultLauncher<Intent>,
onWebViewCreated: (WebView) -> Unit = {}
) {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
WebPage(
url = url,
modifier = Modifier
.fillMaxSize()
.padding(innerPadding),
onFileChoose = { intent ->
filePickerLauncher.launch(intent)
},
onWebViewCreated = onWebViewCreated
)
}
}
@Composable
fun WebPage(
url: String,
modifier: Modifier = Modifier,
onFileChoose: (Intent) -> Unit,
onWebViewCreated: (WebView) -> Unit = {}
) {
Column(modifier = modifier) {
// WebView容器
AndroidView(
modifier = Modifier.weight(1f),
factory = { ctx ->
WebView(ctx).apply {
webViewClient = WebViewClient()
settings.apply {
javaScriptEnabled = true
domStorageEnabled = true
allowFileAccess = true
allowContentAccess = true
mediaPlaybackRequiresUserGesture = false
useWideViewPort = true
loadWithOverviewMode = true
}
onWebViewCreated(this)
webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
FileChooserHelper.setFilePathCallback(filePathCallback)
val intent = fileChooserParams.createIntent().apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
if (fileChooserParams.mode == FileChooserParams.MODE_OPEN_MULTIPLE) {
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
}
}
onFileChoose(intent)
return true
}
}
}
},
update = { webView ->
if (webView.url != url) {
webView.loadUrl(url)
}
}
)
}
}