Android获取图片:拍照和从相册中选择
概述
在Android开发中获取图片主要包括如下两种方式:
- 打开相机拍照
- 从图库中选择图片
一、打开相机拍照
打开相机拍照主要包括如下几个部分:
- 权限申请
- 打开摄像头
- 拍照后传回数据处理
1. 权限申请
如果需要打开相机,则需要申请摄像头使用权限,在AndroidManifest.xml文件中添加如下代码:
<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" />
注意权限申请在Android 6.0以上需要动态申请权限,在此不再赘述。
注意:
Android 10及以上,系统对文件读写的控制更加严格,如果想要粗暴的实现可读写效果,可以在AndroidManifest.xml
中<application>
下配置如下代码达到效果:
android:requestLegacyExternalStorage="true"
2. 打开摄像头
打开摄像头可以通过设置intent来实现,示例如下:
private fun openCamera() {
if (PermissionUtils.instance.checkPermission(this, Manifest.permission.CAMERA)) {
//为intent指定Action
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(intent, IntentUtil.instance.requestCameraPermissionCode)
} else {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), IntentUtil.instance.requestCameraPermissionCode)
}
}
通过为intent指定摄像头Action:MediaStore.ACTION_IMAGE_CAPTURE
,然后直接通过startActivityForResult
的方式启动Intent即可启动摄像头。
3. 拍照后传回数据处理
在拍照结束后,我们可以对拍照结束后的回传的intent及携带的数据进行处理,例如可以将传回的图片以文件的形式保存起来或者直接加载在ImageView中,示例如下:
override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
IntentUtil.instance.requestCameraPermissionCode -> {
var extra = data?.extras
var photo = extra?.get("data") as Bitmap?
if (photo != null) {
var path = FileUtils.instance.getAppDir()
if (path != null) {
ImageUtils.instance.saveBitmapToFile(photo, System.currentTimeMillis().toString() + ".png",
"TestImg", this)
}
imgQRCode?.setImageBitmap(photo)
}
}
else -> {
}
}
}
}
通过在onActivityResult
方法中获取到了intent对象,从其中获取到图片bitmap数据,然后进行对应的图片操作。
二、从相册中选取到图片
从相册中选取图片的实现主要包括如下几个步骤:
- 权限申请
- 打开相册选择图片
- 针对回传的图片uri地址去查询和获取到对应的文件的真实URI
- 根据文件URI获取到图片
1. 权限申请
从相册中选取图片所需要的权限主要包括文件读权限,在AndroidManifest.xml中添加如下代码:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Android 6.0及以上需要注意动态权限的申请,在此不再赘述。
Android 10及以上的处理同上。
2. 打开相册
和打开相机相似,打开相册也可以通过intent启动进行打开,示例代码如下:
private fun openImageUtils() {
val intent: Intent
if (Build.VERSION.SDK_INT < 19) {
intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
} else {
intent = Intent(
Intent.ACTION_PICK,
MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
}
startActivityForResult(intent, IntentUtil.instance.openImageGalleryCode)
}
3. 针对回传数据进行处理
通过startActivityForResult
启动intent,可以在onActivityResult
中得到回传结果,因此可以通过回传的intent得到选择的图片的uri数据,示例代码如下:
override fun onActivityResult(requestCode: Int, resultCode: Int, @Nullable data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
IntentUtil.instance.openImageGalleryCode -> {
var uri = data?.data
if (data != null && uri != null) {
var imgPath = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
ImageUtils.instance.handleImageBeforeKitKat(data, this)
} else {
ImageUtils.instance.handleImageOnKitKat(data, this)
}
displayImage(imgPath)
}
}
else -> {
}
}
}
}
获取到URI之后便可以对URI进行处理并获取到图片,但是需要注意的是Android在4.4前后通过图库选择得到的图片的URI并不是一致的,因此需要分开处理。
Android在4.4之前获取到的URI是真实文件路径,因此不需要做过多的处理。
Android在4.4之后获取到URI并不是文件的真实路径,可能是类似于下面这种路径,因此需要重新对此URI进行解析以拿到真正的文件路径。
com.android.providers.media.documents.image%3A11111
解析代码示例如下:
/**
* 从相册中读取图片,在4.4之后
* @param data:打开图片选择后返回的intent
* @param context
* @return
*/
fun handleImageOnKitKat(data: Intent, context: Context?): String? {
var imagePath: String? = null
val uri = data.data
if (DocumentsContract.isDocumentUri(context, uri)) {
// 如果是document类型的Uri,则通过document id处理
val docId = DocumentsContract.getDocumentId(uri)
if ("com.android.providers.media.documents" == uri!!.authority) {
val id = docId.split(":").toTypedArray()[1] // 解析出数字格式的id
val selection = MediaStore.Images.Media._ID + "=" + id
LogUtils.instance.getLogPrint("id=$id,selection=$selection")
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, context)
} else if ("com.android.providers.downloads.documents" == uri.authority) {
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(docId))
imagePath = getImagePath(contentUri, null, context)
}
} else if ("content".equals(uri!!.scheme, ignoreCase = true)) {
// 如果是content类型的Uri,则使用普通方式处理
imagePath = getImagePath(uri, null, context)
} else if ("file".equals(uri.scheme, ignoreCase = true)) {
// 如果是file类型的Uri,直接获取图片路径即可
imagePath = uri.path
}
return imagePath
}
/**
* 4.4版本以前,直接获取真实路径
* @param data
* @return
*/
fun handleImageBeforeKitKat(data: Intent, context: Context?): String? {
val uri = data.data
return getImagePath(uri, null, context)
}
/**
* 查询图库中是否存在有指定路径的图片
* @param uri:路径URI
* @param selection:筛选条件
* @param context
* @return
*/
private fun getImagePath(uri: Uri?, selection: String?, context: Context?): String? {
var path: String? = null
// 通过Uri和selection来获取真实的图片路径
val cursor: Cursor? = context?.contentResolver?.query(uri!!, null, selection, null, null)
if (cursor != null) {
LogUtils.instance.getLogPrint("cursor不为null $selection")
var i = 0
while (i < cursor.columnCount) {
var ss = cursor.getColumnName(i)
LogUtils.instance.getLogPrint("$i $ss")
i++
}
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA))
LogUtils.instance.getLogPrint("get path= $path")
}
cursor.close()
}
return path
}
最终通过cursor进行查询并获取到具体的图片文件地址。
4. 获取图片并处理
经过上步之后,我们可以获取到对应图片文件地址,可以通过过文件操作获取文件并添加到ImageView等操作,示例如下:
private fun displayImage(imgPath: String?) {
if (imgPath != null && FileUtils.instance.isFileExist(imgPath)) {
var bitmap = BitmapFactory.decodeFile(imgPath)
imgQRCode?.setImageBitmap(bitmap)
}
}
总结
获取图片的两种方式在实现上有相似的思路,都是经过如下几步:
- 申请权限
- 设置对应的intent Action,通过intent跳转到对应界面
- 在ActivityResults中处理返回的数据结果,获取到b图片对象。
需要主要的内容包括如下几点:
- 权限申请需要注意6.0以上的权限申请以及Android 10以上对于文件读写的控制。
- 打开图库选取照片要注意你当前从intent中获取的uri的格式,不仅仅需要考虑Android sdk版本的不同,同时对于不同的手机机型也有可能返回不同的uri路径,因此存在有单独适配的情况。