博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android实现本地图片上传功能,H5调用本地相册/相机上传图片
阅读量:5856 次
发布时间:2019-06-19

本文共 4831 字,大约阅读时间需要 16 分钟。

877eb9ace1d3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

在开发中有时候会用到H5调用本地图片或者相机,像第三方的实名认证,在线客服等等都需要上传图片。H5中只需要通过调用即可,ios是可以的实现的,不需要自己处理,除非客户端压缩图片,但是Android中需要自己处理,比较坑,本文记录H5调用本地图片或者相机的实现过程以及遇到的问题。

先看效果图

877eb9ace1d3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

H5

H5主要是通过input标签来获取图片

Android

通过WebView加载h5页面,监听WebView对应的方法,实现自己的逻辑。

WebView初始化,放开对应的权限

private fun initWebView() {

val settings = mWebView.settings

settings.javaScriptEnabled = true

settings.javaScriptCanOpenWindowsAutomatically = true

//自适应屏幕

settings.layoutAlgorithm = SINGLE_COLUMN

settings.loadWithOverviewMode = true

settings.domStorageEnabled = true

settings.databaseEnabled = true

settings.setSupportZoom(false)

mWebView.isHorizontalScrollBarEnabled = false

mWebView.isVerticalScrollBarEnabled = false

mWebView.webViewClient = webViewClient

mWebView.webChromeClient = webViewChromeClient

}

设置WebChromeClient

当点击input标签的时候会调用WebChromeClient的onShowFileChooser()方法(5.0+)或者openFileChooser()方法(3.0+)在改方法中处理自己逻辑,这里是弹出一个原生的选择框,选择从相册还是拍照获取图片。

private val webViewChromeClient = object : WebChromeClient() {

override fun onShowFileChooser(webView: WebView, filePathCallback: ValueCallback>,

fileChooserParams: FileChooserParams): Boolean {

mFilePathCallback = filePathCallback

val acceptTypes = fileChooserParams.acceptTypes

//判断文件类型

if (acceptTypes.contains("image/*")) {

showSelectDialog()

}

return true

}

}

ValueCallbacks是WebView组件提供给我们的,它里面包含了一个或者一组Uri,然后我们在onActivityResult()里将Uri传给ValueCallbacks的onReceiveValue()方法,这样就把文件回传给H5端了。

注意:这里会遇到onShowFileChooser()只调用一次的问题,也就是第二次点击按钮的时候该方法不再调用,因为每次调用该方法都需要设置一个回调,不管是不是选择了照片,没有就回传一个null

/**

* 显示相册/拍照选择对话框

*/

private fun showSelectDialog() {

if (mSelectPhotoDialog == null) {

mSelectPhotoDialog = SelectDialog(this, View.OnClickListener { view ->

when (view.id) {

R.id.tv_camera -> startCamera()

R.id.tv_photo -> startAlbum()

//不管选择还是不选择,必须有返回结果,否则就会调用一次

R.id.tv_cancel -> {

mFilePathCallback?.onReceiveValue(null)

mFilePathCallback = null

}

}

})

}

mSelectPhotoDialog?.show()

}

拍照和打开相册功能封装到了PhotoUtils工具类中了。

/**

* 拍照

* @param context Activity

*/

fun startCamera(context: Activity) {

val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

PATH_PHOTO = getSdCardDirectory(context) + "/temp.png"

val temp = File(PATH_PHOTO)

if (!temp.parentFile.exists()) {

temp.parentFile.mkdirs()

}

if (temp.exists()) {

temp.delete()

}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

//添加这一句表示对目标应用临时授权该Uri所代表的文件

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)

// 通过FileProvider创建一个content类型的Uri

val uri: Uri = FileProvider.getUriForFile(context, context.packageName + ".fileprovider", temp)

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri)

} else {

intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(temp))

}

context.startActivityForResult(intent, RESULT_CODE_CAMERA)

}

注意:拍照需要适配7.0,否者拿不到文件,Android 7.0 以后 通过FileProvider在应用间共享文件;此外拍完照片获取文件的时候不要通过intent来获取数据,直接通过这里指定的存储路径来获取,intent获取的一直是null

......

android:name="androidx.core.content.FileProvider"

android:authorities="com.gfd.h5pic.fileprovider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/file_paths" />

file_paths.xml

name="cache"

path="" />

name="sdcard_files"

path="" />

name="external"

path="" />

name="external_files"

path="" />

name="external_cache"

path="" />

name="ex_sdcard_files"

path="" />

name="camera_has_sdcard"

path="file/" />

name="camera_no_sdcard"

path="" />

从相册中选择

/**

* 打开相册

* @param context Activity

*/

fun startAlbum(context: Activity) {

val albumIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

albumIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*")

context.startActivityForResult(albumIntent, RESULT_CODE_PHOTO)

}

在onActivityResult()方法中通过intent获取选取的照片。

onActivityResult()方法处理

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

super.onActivityResult(requestCode, resultCode, data)

if (requestCode == PhotoUtils.RESULT_CODE_CAMERA && resultCode == Activity.RESULT_OK) {

//拍照并确定

compressPicture(PhotoUtils.PATH_PHOTO)

} else if (requestCode == PhotoUtils.RESULT_CODE_PHOTO && resultCode == Activity.RESULT_OK) {

//相册选择并确定

val result = data?.data

val path = result?.let { PhotoUtils.getPath(this, it) }

if (path == null) {

mFilePathCallback?.onReceiveValue(null)

} else {

compressPicture(path)

}

} else {

mFilePathCallback?.onReceiveValue(null)

}

}

这里需要注意的是要判断resultCode,不然拍照完成,选择的不是确定而是取消,也是会上传图片的。

这里使用Luban压缩以后再上传的

/**

* 压缩图片

*/

private fun compressPicture(path: String) {

PhotoUtils.compressPicture(this, path, object : PhotoUtils.OnPictureCompressListener() {

override fun onSuccess(file: File) {

//上传图片

mFilePathCallback?.onReceiveValue(arrayOf(Uri.fromFile(file)))

}

override fun onError(e: Throwable) {

mFilePathCallback?.onReceiveValue(null)

}

})

}

最后附上完整的代码

转载地址:http://bqajx.baihongyu.com/

你可能感兴趣的文章
适用于 Windows 的虚拟机扩展和功能
查看>>
【statistics】查看2018.4.25日理想论坛某ID在一天中的活跃情况
查看>>
onmouseover和onmouseout应用于RadioButtonList或CheckBoxList
查看>>
HDU 1016 Prime Ring Problem
查看>>
VB.NET Winform的一些功能实现
查看>>
【自动化测试】Python 读取.yaml格式文件
查看>>
Scrapy-Cluster结合Spiderkeeper管理分布式爬虫
查看>>
YII分模块加载路由
查看>>
关于exprot 和expert default的区别
查看>>
智能合约设计模式--COP
查看>>
【丛林】CSS 边框浅谈
查看>>
[踩坑] ios版的webview无法长按识别二维码
查看>>
JavaScript面向对象编程——原型
查看>>
搞懂QuickBI的OLAP引擎技术原理,人人都是数据分析师
查看>>
React 深入系列6:高阶组件
查看>>
调试时经常使用的console.log()的同步和异步问题
查看>>
Android事件管理源码剖析
查看>>
Docker技术浅谈:私有化部署的优势以及在顶象内部的应用实践
查看>>
跨域问题汇总
查看>>
小技巧|CSS如何实现文字两端对齐
查看>>