Android 10及以上 调用相机及相册选择 压缩图片 删除图片

目录

前言

效果图

调用相机

相册选择

压缩图片

删除图片

总结


前言

        许多博客已经列举了该方面的知识,本文主要针对压缩、删除图片展开使用。                       

        测试通过 > 实机设备: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及以上 调用相机及相册选择 压缩图片 删除图片icon-default.png?t=N7T8https://blog.csdn.net/m0_66984757/article/details/138959624

  • 11
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值