目录
前言
许多博客已经列举了该方面的知识,本文主要针对压缩、删除图片展开使用。
测试通过 > 实机设备:Xiaomi Pad 5 Pro Android版本: Andorid11
如果不需要删除图片,推荐直接看 第一行代码-第二版(郭霖著)笔记八(通知和多媒体),这位作者学习郭霖大佬写的调用摄像头和相册就够用了。
效果图
Android10以上调用相机 相册选择
调用相机
调用相机和相册的权限记得都弄进去,还有动态判断权限啥的,不清楚查阅相关资料。
系统相机拍摄的图片存储在:内部存储/DCIM/Camera/文件名 【内部存储是/storage/emulated/0】我们调用相机拍的照片和相册选择时压缩图片后新图片,这两种APP新建的图片路径可以自定义到指定路径,我这里存储在:内部存储/Pictures/文件名
1、点击拍照,调用相机: CurrentImageFile = CameraUtil.TakePhoto(MainActivity.this)
public class CameraUtil {
static File currentImageFile = null;
/** 调用相机 **/
public static File TakePhoto(Activity activity){
// 这样创建的图片只会存储在Android/data/com.android.lb_mes文件夹里
File dir = AppContext.getContext().getExternalFilesDir("pictures"); // 获取应用私有的外部存储目录
if (dir.exists()) dir.mkdirs();
currentImageFile = new File(dir, System.currentTimeMillis() + ".jpg");
if(currentImageFile.exists()){
try{
currentImageFile.createNewFile();
} catch(IOException e){
e.printStackTrace();
}
}
Uri imageUri = null;
// 第二个参数对应AndroidManifest.xml中provider属性android:authorities的值
imageUri = FileProvider.getUriForFile(activity, "com.android.lb_mes.fileprovider",currentImageFile);
Intent it = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
it.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
activity.startActivityForResult(it, 1);
return currentImageFile;
}
}
记得要在AndroidManifest.xml文件里配置provider
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.android.lb_mes.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
@xml/provider_paths 在res目录下建一个provider_paths.xml文件
文件内容:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
</paths>
拍完照之后,在onActivityResult里可以拿返回的currentImageFile来获取Bitmap。
private File currentImageFile = null;//定义一个保存图片的 File 变量
private Bitmap mBitmap = null;
// 重写onActivityResult方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1){
mBitmap = CameraUtil.getBitmapFromUri(Uri.fromFile(currentImageFile), MainActivity.this); // 拿到mBitmap,用于显示和保存到内部存储
CameraUtil.saveImageToMediaStore(mBitmap, fileName);// 保存图片到系统相册里
}
}
public class CameraUtil{
/** Uri转Bitmap **/
public static Bitmap getBitmapFromUri(Uri uri, Activity activity) {
try {
ParcelFileDescriptor r = activity.getContentResolver().openFileDescriptor(uri, "r");
Bitmap bitmap = BitmapFactory.decodeFileDescriptor(r.getFileDescriptor());
return bitmap;
} catch (FileNotFoundException exception) {
exception.printStackTrace();
}
return null;
}
}
拿到mBitmap就可以通过Handler设置图片显示。本文主要是保存图片到系统相册,因为通过AppContext.getContext().getExternalFilesDir("pictures") 拍照的图片是存储在当前APP的Data文件夹里的,相当于缓存,就跟SQlite缓存数据一样。
保存图片: saveImageToMediaStore(Bitmap bitmap, String imageName),使用 MediaStore API 保存图片到内部存储,将Bitmap转字节数组,再保存到指定路径当中。
// Bitmap转字节数组
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); // 100 is the quality factor
byte[] imageBytes = byteArrayOutputStream.toByteArray();
这里直接调用bitmap.compress方法,将bitmap转字节数组的输出流,再转成字节数组;
建立字节数组缓存ContentValues
// 字节数组保存
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, imageName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator);
// 自定义文件路径
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "/Camera/其他");
// 对于 Android 10 (API 29) 及以上版本,使用 MediaStore API
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentResolver contentResolver = AppContext.getContext().getContentResolver();
Uri imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
if (imageUri != null) {
try (OutputStream os = contentResolver.openOutputStream(imageUri)) {
os.write(imageBytes);
os.flush();
// 成功保存后,可以通知媒体扫描器扫描新文件
MediaScannerConnection.scanFile(AppContext.getContext(), new String[]{imageUri.toString()}, null, null);
} catch (IOException e) {
e.printStackTrace();
}
}
// 对于 Android 10 以下的版本,你可能需要使用传统的方法保存文件
}
到此从调用相机到保存图片到相册里就结束了。题外话:Android10引入了Scoped Storage机制,App被禁止使用绝对路径访问公共存储空间。所以App想要访问图片、视频等资源的话,是需要借助MediaStore API来完成。大部分操作也是围绕MediaStore API来进行。
相册选择
//进入相册
// Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// startActivityForResult(intent, 0);
这种方式直接进入相册选择图片,然后返回data,同样Android10以后安全与隐私加强以后,这种方式不方便对图片进行操作或删除,但是不需要删除选择的图片,用这种方式就挺好的,相册展示图片比较美观。
这里我使用文件选择器显示图片进行选择
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, 0);
// 重写onActivityResult
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
String fileCode = format.format(new Date());
String fileName = "IMG_" + fileCode + ".jpg";
if (requestCode == 0) {
Uri result = data != null ? data.getData() : null;
Log.i("onActivityResult","result:" + result);
if (result != null) {
try {
// 调用系统相册获取选中图片路径
pic_path = CameraUtil.getRealFilePath(this, result);
Log.i("onActivityResult","pic_path:" + pic_path);
mBitmap = CameraUtil.getBitmapFromUri(result, MainActivity.this);
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
}
}
在相册里选择图片后返回的data我们可以获取到Uri,再根据Uri拿到图片的path,可以存起来上传的时候用到,依然跟调用相机时一样,使用Uri转Bitmap方法,拿到Bitmap再显示到页面上就OK了
压缩图片
当图片占用内存比较大,好多MB的时候,需要进行压缩一下。
Uri result = data != null ? data.getData() : null; // 选中相册的图片Uri
Bitmap bitmap = CameraUtil.getBitmapFromUri(result, MainActivity.this);// uri转bitmap
Uri compressedUri = CameraUtil.compressImage(getBaseContext(),bitmap,"new_file.jpg",Bitmap.CompressFormat.JPEG, 30);// 压缩 [last参数是0-100,100是不压缩,值越小压缩越狠]
mBitmap = CameraUtil.getBitmapFromUri(compressedUri,MainActivity.this);// 得到压缩uri转的bitmap
/** 压缩图片 quality压缩比例**/
public static Uri compressImage(Bitmap bitmap, String fileName, int quality) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
ContentResolver contentResolver = AppContext.getContext().getContentResolver();// 获取ContentResolver
Uri uri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
try (OutputStream outputStream = contentResolver.openOutputStream(uri)) {
if (outputStream != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, outputStream);
}
} catch (Exception e) {
e.printStackTrace();
}
return uri;
}
删除图片
因为压缩是生成一张新的图片,而原图片还在,依次类推,选一次图片压缩一次图片,压缩一次生成一张新图片,所以图片会越来越多。
选择图片——压缩图片——生成压缩后图片——删除压缩前原图
删除这一块我查阅了许多资料,也有许多方法,大部分都能够使用,唯一我遇到的就是直接用系统相机拍照,再在APP上打开相册,就删除不了,进入文件选择器也是,知道使用DocumentFile类,可能这个类在文件选择器里权限足够吧,有其他方法的朋友可以在评论区告知一番多谢,还是想打开相册操作。
Uri result = data != null ? data.getData() : null; // 选中相册的图片Uri
//在压缩后执行删除方法
int res = CameraUtil.delete_file(result);
Toast.makeText(this, "删除了" + res + "条数据!", Toast.LENGTH_SHORT).show();
public static int delete_file(Uri fileUri){
int res = 0;
Log.e("delete_file","fileUri:" + fileUri);
if(fileUri != null){
ContentResolver contentResolver = AppContext.getContext().getContentResolver();
DocumentFile documentFile = DocumentFile.fromSingleUri(AppContext.getContext(), fileUri);
if (documentFile != null && documentFile.exists() && documentFile.canWrite()) {
boolean deleted = documentFile.delete();// 删除文件
if(deleted){
res++;
Log.e("delete_file","deleted success");
} else {
Log.e("delete_file","deleted false");
}
} else {
Log.e("delete_file","无法写入文件或文件不存在");
}
}
return res;
}
还有两种删除操作是:
1、拿到相册图片目录,比较图片文件名,使用file.delete()方法。
2、用ContentResolver查询imageId,再查出imageUri,用contentResolver.delete(imageUri,null,null)删除图片。
这两种不能删除系统相机直接拍照的图片,这里就不赘述了。
总结
唯一的缺陷是调用相册时不是打开相册,暂时还没找到直接打开相册把选择图片删掉的方法,只能绕弯打开文件选择器去删除图片,以后遇到再补吧。有朋友知道也可以评论区留言一番。
转载请注明出处:Android 10及以上 调用相机及相册选择 压缩图片 删除图片https://blog.csdn.net/m0_66984757/article/details/138959624