主要解决有两个问题
一、Android 调用MediaStore.Images.Media.insertImage保存图片时生成两张图片的问题
解决方案:
/*// MediaStore.Images.Media.insertImage支持我们传递进去一个图片文件路径,不过我并不推荐使用这个方法,因为它会将原本的图片,再 Copy 一份,
// 到 Picture 目录下,也就是说你最终在磁盘上会得到两张相同的图片。
MediaStore.Images.Media.insertImage(contentResolver,
file.absolutePath, file.name, null)*/
// 保存图片替换方式
var values = ContentValues()
values.put(MediaStore.Images.Media.DATA, file.absolutePath)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
var uri =contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
二、三种方法,刷新 Android 的 MediaStore!让你保存的图片立即出现在相册里!
Android扫描多媒体文件剖析
我推荐的方法,是使用 MediaScannerConnection 来实现。通过scanFile()方法,我们只需要制定一个待刷新的文件路径和对应的 MimeType 即可,它支持传递多个路径,也可就是支持批量扫描。
注意这里的 MimeType 是一定要填写的,并且不能写通配符*/*或null,否则会导致刷新失败,通常我们保存的是一个图片的话,只需要传递image/jpeg即可。
MediaScannerConnection.scanFile(this , arrayOf(picFile.absolutePath) , arrayOf("image/jpeg"), {path, uri -> Log.i("cxmyDev","onScanCompleted : "+path)})
三、保存图片到相册
1、// 保存图片到相册方式一: 不支持我们指定路径,自动将图片保存至【Picture】目录下,更新MediaStore
private fun downloadPic1(pic: String?) {
Flowable.just(pic)
.map{ s: String?-> saveImage1(s)}
.subscribeOn(Schedulers.io())//在io线程执行订阅内容
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())//在Android主线程执行观察
.subscribe(Consumer {
if (it.isNullOrBlank()) {
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()
}else {
/*var fileUri = Uri.fromFile(File(it))
sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, fileUri))*/
MediaScannerConnection.scanFile(mContext, arrayOf(it), arrayOf("image/jpeg"), null)
Toast.makeText(this, "已保存在 ".plus(it), Toast.LENGTH_SHORT).show()
}
}, Consumer {
Log.e(TAG, "onLongClick: 保存图片到相册 e = " +it.message)
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()
})
}
@Throws(IOException::class, ExecutionException::class, InterruptedException::class)
private fun saveImage1(data: String?): String? {
val target: FutureTarget = Glide.with(this)
.asBitmap()
.load(data)
.submit()
var bitmap = target.get()
if (bitmap !=null) {
// 使用 Media.inserImage() 方法,不需要我们指定路径,会自动将图片保存至 Picture 目录下。
// 它也不支持我们指定路径。如果我们对图片保存的路径没有要求,并且保存的是一个 Bitmap 对象,此方法是非常的方便的。
var stringUrl = MediaStore.Images.Media.insertImage(contentResolver,
bitmap, data, data)
bitmap?.recycle()
/**
* 取保存的图片地址
*/
var url = Uri.parse(stringUrl)
var result: String?
var cursor = MediaStore.Images.Media.query(contentResolver, url, arrayOf(MediaStore.Images.ImageColumns.DATA),//
null, null, null)
return if (cursor ==null)
url.path
else {
cursor.moveToFirst()
var index = cursor?.getColumnIndex(MediaStore.Images.ImageColumns.DATA)
result = cursor.getString(index)
cursor.close()
result
}
}
return ""
}
2、// 保存图片到相册方式二: 支持指定路径,更新MediaStore
private fun downloadPic2(pic: String?) {
Flowable.just(pic)
.map{ s: String?-> saveImage2(s)}
.map{ file: File?->
return@map file?.let {
/*// MediaStore.Images.Media.insertImage支持我们传递进去一个图片文件路径,不过我并不推荐使用这个方法,因为它会将原本的图片,再 Copy 一份,
// 到 Picture 目录下,也就是说你最终在磁盘上会得到两张相同的图片。
MediaStore.Images.Media.insertImage(contentResolver,
file.absolutePath, file.name, null)*/
// 保存图片替换方式
var values = ContentValues()
values.put(MediaStore.Images.Media.DATA, file.absolutePath)
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
/*// 正常情况下,它是没有问题的,不过假如你发现它不生效,就需要检查一下你文件的路径是否传递正确。通过查看 MediaScannerReceiver 的源码,
// 可以发现 onReceive() 方法中,针对 ACTION_MEDIA_SCANNER_SCAN_FILE 还有一个限制条件,那就是传递进去的文件绝对路径,
// 必须是以 Environment.getExternalStorageDirectory() 方法的返回值开头。
sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)))*/
// 刷新 MediaStore 替换方式:
// 刷新 MediaStore 还有一个最通用也是我推荐的一个方法,那就是使用 MediaScannerConnection 进行操作。
// 不同于 MediaStore.Image.Media 和广播的方式,使用 MediaScannerConnection 不仅可以保存文件,还可以指定文件路径,最好的就是,它还支持刷新完成的回调。
// 如果我们对时序有要求,并且需要制定文件保存路径的话,最好的方式就是直接使用 MediaScannerConnection 类进行操作,并且这也应该是兼容最好的方式。
// 这里我们主要是利用 MediaScannerConnection 类的 scanFile() 方法进行触发扫描
MediaScannerConnection.scanFile(mContext, arrayOf(file.absolutePath), arrayOf("image/jpeg"), null)
file
} ?:null
}
.subscribeOn(Schedulers.io())//在io线程执行订阅内容
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())//在Android主线程执行观察
.subscribe({ file: File?->
file?.let {
Toast.makeText(this, "已保存在 ".plus(file.absoluteFile), Toast.LENGTH_SHORT).show()
} ?: Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()
}){ throwable: Throwable->
Log.e(TAG, "onLongClick: 保存图片到相册 e = " + throwable.message)
Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show()
}
}
@Throws(IOException::class, ExecutionException::class, InterruptedException::class)
private fun saveImage2(data: String?): File? {
val fos: FileOutputStream
var file: File? =null
val target: FutureTarget = Glide.with(this)
.asBitmap()
.load(data)
.submit()
var bitmap = target.get()
if (bitmap !=null) {
file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(Date()) +".jpg")
fos = FileOutputStream(file)
bitmap?.compress(Bitmap.CompressFormat.JPEG, 80, fos)
fos.flush()
fos.close()
bitmap?.recycle()
}
return file
}