文档说明

在日常开发过程中,我们经常需要用到调用相册和相机拍照功能,由于系统版本混乱,使用方式杂乱不一,在此进行整理说明以及简单的原理分析。

注意权限声明:

简单使用

通过调用相机

private fun openCamera() {
val takePhotoIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takePhotoIntent.resolveActivity(packageManager) != null) { // 解析是否存在可以处理当前意图的Activity
startActivityForResult(
takePhotoIntent,
PERSONAL_INFO_I_TAKEPHOTO
);//启动相机
}
}
通过相册
private fun openAlbum() {
val openAlbumIntent = Intent(Intent.ACTION_GET_CONTENT)
openAlbumIntent.type = "image/*"
startActivityForResult(openAlbumIntent, PERSONAL_INFO_I_GALLEY) //打开相册
}
处理返回结果
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
PERSONAL_INFO_I_TAKEPHOTO -> {
if (resultCode == RESULT_OK) {
val bitmap: Bitmap = data?.getParcelableExtra("data")!! // intent中data字段就是保存的是bitmap缩略图
val tempUri :Uri = getImageUri(bitmap)
}
}
PERSONAL_INFO_I_GALLEY -> {
if (resultCode == RESULT_OK) {
val tempUri :Uri = data!!.data // intent中data字段就是保存的uri
}
}
}
// 内存图片保存为uri
fun getImageUri(inImage: Bitmap): Uri {
val bytes = ByteArrayOutputStream()
inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes)
val path = MediaStore.Images.Media.insertImage(this.contentResolver, inImage, "Title", null)
return Uri.parse(path)
}

拍照获取原图

原理说明

通过以上的实现方式,相机实现的是拍照的缩略图,如何取到实际拍摄的原图呢?

在这里需要用到 Android provider 实现方式。

首先在清单文件添加以下逻辑:

...
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider" // 建议更新为 `包名.fileprovider`
android:exported="false"
android:grantUriPermissions="true">
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
 // 记得新建xml文件 
 
...
file_paths.xml 文件内容:
name="my_images"
path="." />

字段名表示需要创建uri的文件夹,常用的有 external-path,files-path 和 cache-path。external-path表示的含义是通过Environment.getExternalStorageDirectory() 获取的文件存储位置,files-path表示通过 Context.getFilesDir() 获取的文件存储位置,cache-path表示通过Context.getCacheDir()获取的文件存储位置。

name属性表示这个文件的昵称,会包含在生成的uri中,path表示指定的子文件夹。

配置结束生成的 uri 类似 content://com.example.testad.fileprovider/my_images/xxx

使用方式

lateinit var currentPhotoPath: String // 保存文件路径
lateinit var photeUri: Uri // 保存照片的uri
private fun createImageFile(): File {
val storageDir: File? = this.filesDir // 目标存储在 files 文件夹内
return File.createTempFile(
"JPEG_${System.currentTimeMillis()}_", // 文件名
".jpg", // 后缀
storageDir // 存储文件夹
).apply {
currentPhotoPath = absolutePath
}
}
private fun takePhote() {
Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
takePictureIntent.resolveActivity(packageManager)?.also {
val photoFile: File = createImageFile()
photoFile.also {
photeUri = FileProvider.getUriForFile( // 根据文件创建uri
this,
"com.example.testad.fileprovider", // 在清单文件声明的权限全称
it
)
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photeUri) // 传入目标uri
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO)
}
}
}
}
处理结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_TAKE_PHOTO -> {
if (resultCode == RESULT_OK) {
photeUri // 直接使用上面生成的uri
}
}
}
}

在官方文档中说明了 MediaStore.ACTION_IMAGE_CAPTURE 的使用方式,如果没有传入 MediaStore.EXTRA_OUTPUT 则默认返回一个小尺寸的 Bitmap 对象,反之则将图片全尺寸进行写入传递的位置。具体说明参考:ACTION _ IMAGE _ CAPTURE

到此为止就已完成我们的需求,如果需要拍照插入图库,下面步骤即可实现:

private fun galleryAddPic() {
Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent ->
val f = File(currentPhotoPath) // 需要传递文件真实位置,系统会生成标准的uri
mediaScanIntent.data = Uri.fromFile(f)
sendBroadcast(mediaScanIntent)
}
}

以上便是 Android 拍照的简单使用。

如有疑问,欢迎交流讨论。