Web拍照在Android内的WebView中实现
使用Android Studio开发的一个应用内的webview页面需要拍照上传功能
因为我对安卓开发不太熟悉所以借助gpt的力量获得了这些代码但其中有一些问题需要修正
1|0一 权限处理
`
<uses-feature
android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
`
2|0二 设置 WebView 允许文件访问和 JavaScript
`
val webView: WebView = findViewById(R.id.webview)
val webSettings = webView.settings
webSettings.javaScriptEnabled = true
webSettings.allowFileAccess = true
webSettings.domStorageEnabled = true
webSettings.allowContentAccess = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
`
3|0三 实现 WebChromeClient 处理文件选择
`
webView.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
if (mFilePathCallback != null) {
mFilePathCallback?.onReceiveValue(null)
}
mFilePathCallback = filePathCallback
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePictureIntent.resolveActivity(packageManager)?.let {
var photoFile: File? = null
try {
photoFile = createImageFile()
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath)
} catch (ex: IOException) {
Log.e("WebView", "Image file creation failed", ex)
}
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.absolutePath
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile))
}
}
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
}
val intentArray = takePictureIntent?.let { arrayOf(it) } ?: arrayOf<Intent>()
val chooserIntent = Intent(Intent.ACTION_CHOOSER).apply {
putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
putExtra(Intent.EXTRA_TITLE, "Image Chooser")
putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
}
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE)
return true
}
}
}
`
4|0四 处理 onActivityResult 返回的结果
`
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
val results: Array<Uri>? = when {
resultCode != Activity.RESULT_OK -> null
data == null -> mCameraPhotoPath?.let { arrayOf(Uri.parse(it)) }
else -> data.dataString?.let { arrayOf(Uri.parse(it)) }
}
mFilePathCallback?.onReceiveValue(results)
mFilePathCallback = null
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
`
5|05. 创建图片文件(这里创建文件保存路径为私有目录路径导致返回给WebView的值为null, 改为公共路径之后就可以了)
`
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val imageFileName = "JPEG_" + timeStamp + "_"
val storageDir: File = getExternalFilesDir(Environment.DIRECTORY_PICTURES) ?: throw IOException("Failed to get storage directory")
return File.createTempFile(imageFileName, ".jpg", storageDir).apply {
mCameraPhotoPath = absolutePath
}
}
//修改后
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
return File.createTempFile("JPEG_${timeStamp}_", ".jpg", storageDir).apply {
mCameraPhotoPath = "file:$absolutePath"
}
}
`
6|0完整代码
`
class MyWebViewActivity : AppCompatActivity() {
private var mFilePathCallback: ValueCallback<Array<Uri>>? = null
private var mCameraPhotoPath: String? = null
private val INPUT_FILE_REQUEST_CODE = 1
private val CAMERA_PERMISSION_REQUEST_CODE = 2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val webView: WebView = findViewById(R.id.webview)
val webSettings = webView.settings
webSettings.javaScriptEnabled = true
webSettings.allowFileAccess = true
webSettings.domStorageEnabled = true
webSettings.allowContentAccess = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
webView.webChromeClient = object : WebChromeClient() {
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
if (mFilePathCallback != null) {
mFilePathCallback?.onReceiveValue(null)
}
mFilePathCallback = filePathCallback
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
takePictureIntent.resolveActivity(packageManager)?.let {
var photoFile: File? = null
try {
photoFile = createImageFile()
takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath)
} catch (ex: IOException) {
Log.e("WebView", "Image file creation failed", ex)
}
if (photoFile != null) {
mCameraPhotoPath = "file:" + photoFile.absolutePath
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile))
}
}
val contentSelectionIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
}
val intentArray = takePictureIntent?.let { arrayOf(it) } ?: arrayOf<Intent>()
val chooserIntent = Intent(Intent.ACTION_CHOOSER).apply {
putExtra(Intent.EXTRA_INTENT, contentSelectionIntent)
putExtra(Intent.EXTRA_TITLE, "Image Chooser")
putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray)
}
startActivityForResult(chooserIntent, INPUT_FILE_REQUEST_CODE)
return true
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == INPUT_FILE_REQUEST_CODE && mFilePathCallback != null) {
val results: Array<Uri>? = when {
resultCode != Activity.RESULT_OK -> null
data == null -> mCameraPhotoPath?.let { arrayOf(Uri.parse(it)) }
else -> data.dataString?.let { arrayOf(Uri.parse(it)) }
}
mFilePathCallback?.onReceiveValue(results)
mFilePathCallback = null
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
@Throws(IOException::class)
private fun createImageFile(): File {
val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val storageDir: File = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
return File.createTempFile("JPEG_${timeStamp}_", ".jpg", storageDir).apply {
mCameraPhotoPath = "file:$absolutePath"
}
}
private fun requestCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_REQUEST_CODE)
}
}
}
`
__EOF__

本文作者:朝闻道-夕可死
本文链接:https://www.cnblogs.com/zhaowendao233/p/18393036.html
关于博主:编程小萌新一名,希望从今天开始慢慢提高,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
本文链接:https://www.cnblogs.com/zhaowendao233/p/18393036.html
关于博主:编程小萌新一名,希望从今天开始慢慢提高,一步步走向技术的高峰!
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理