当前位置: 首页 > article >正文

Android -- 调用系统相册之图片裁剪保存

前言

最近线上反馈,部分vivo手机更换头像时调用系统相册保存图片失败,经本人测试,确实有问题。

经修复后,贴出这块的代码供小伙伴们参考使用。

功能

更换头像选择图片:

  • 调用系统相机拍照,调用系统图片裁剪并保存。
  • 调用系统相册选择照片,调用系统图片裁剪并保存。

此功能需要动态申请 相机和读写外部存储的权限,此处省略了,请自行动态申请添加。

String[] permissions=new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};

1、布局文件activity_picture.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/takePictureFromCamera"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="拍照" />

    <Button
        android:id="@+id/takePictureFromLib"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="从相册选取" />

    <ImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"/>
</LinearLayout>

2、PictureActivity:

public class PictureActivity extends AppCompatActivity {
    public class Const {
        public static final int PHOTO_GRAPH = 1;// 拍照
        public static final int PHOTO_ZOOM = 2; // 相册
        public static final int PHOTO_RESOULT = 3;// 结果
        public static final String IMAGE_UNSPECIFIED = "image/*";
    }

    public String authority;
    private ImageView imageView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_picture);
        authority = getApplicationContext().getPackageName() + ".fileprovider";
        imageView = findViewById(R.id.img);
        findViewById(R.id.takePictureFromCamera).setOnClickListener(
                v -> openCamera(Const.PHOTO_GRAPH));

        findViewById(R.id.takePictureFromLib).setOnClickListener(
                v -> openCamera(Const.PHOTO_ZOOM));

    }

    private void openCamera(int type) {
        Intent intent;
        if (type == Const.PHOTO_GRAPH) {
            //打开相机
            intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            //指定调用相机拍照后照片的储存路径
            File photoFile = new File(CoreConstants.getNurseDownloadFile(this), "temp.jpg");
            if (!photoFile.exists()) {
                photoFile.getParentFile().mkdirs();
            }
            Uri uri = FileUtil.getUri(this, authority, photoFile);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        } else {
            //打开相册
            intent = new Intent(Intent.ACTION_PICK, null);
            intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, Const.IMAGE_UNSPECIFIED);
        }
        startActivityForResult(intent, type);
    }

    /**
     * 调用系统的裁剪图片
     */
    private void crop(Uri uri) {
        try {
            Intent intent = new Intent("com.android.camera.action.CROP");
            String contentURl = CoreConstants.getNurseDownloadFile(this)
                    + File.separator + "temp.jpg";
            File cropFile = new File(contentURl);
            Uri cropUri;
            //在7.0以上跨文件传输uri时候,需要用FileProvider
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                cropUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", cropFile);
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            } else {
                cropUri = Uri.fromFile(cropFile);
            }
            intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri);

            intent.setDataAndType(uri, Const.IMAGE_UNSPECIFIED);
            intent.putExtra("crop", "true");
            // 裁剪框的比例,1:1
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);
            // 裁剪后输出图片的尺寸大小
            intent.putExtra("outputX", 200);
            intent.putExtra("outputY", 200);
            intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());// 图片格式
            intent.putExtra("noFaceDetection", true);// 取消人脸识别
            intent.putExtra("return-data", true);//是否返回裁剪后图片的Bitmap
            intent.putExtra("output", cropUri);
            //重要!!!添加权限,不然裁剪完后报 “保存时发生错误,保存失败”
            List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent,
                    PackageManager.MATCH_DEFAULT_ONLY);
            for (ResolveInfo resolveInfo : resInfoList) {
                String packageName = resolveInfo.activityInfo.packageName;
                grantUriPermission(packageName, cropUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
            ComponentName componentName = intent.resolveActivity(getPackageManager());
            if (componentName != null) {
                // 开启一个带有返回值的Activity,请求码为PHOTO_REQUEST_CUT
                startActivityForResult(intent, Const.PHOTO_RESOULT);
            }
        } catch (Exception e) {
            String s = e.getMessage().toString();
        }
    }

    public Bitmap convertUriToBitmap(Uri uri) {
        ContentResolver contentResolver = getContentResolver();
        try {
            // 将Uri转换为字节数组
            return BitmapFactory.decodeStream(contentResolver.openInputStream(uri));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 拍照
        if (requestCode == Const.PHOTO_GRAPH) {
            // 设置文件保存路径
            File picture = new File(CoreConstants.getNurseDownloadFile(this)
                    + File.separator + "temp.jpg");
            Uri uri = FileUtil.getUri(this, authority, picture);
            crop(uri);
        }
        if (data == null)
            return;

        //读取相册图片
        if (requestCode == Const.PHOTO_ZOOM) {
            crop(data.getData());
        }

        //处理裁剪后的结果
        if (requestCode == Const.PHOTO_RESOULT) {
            Bundle extras = data.getExtras();
            Bitmap photo = null;
            if(extras != null) {
                photo = extras.getParcelable("data");
            }
            if (photo == null && data.getData() != null) {
                //部分小米手机extras是个null,所以想拿到Bitmap要转下
                photo = convertUriToBitmap(data.getData());
            }
            if (photo != null) {
                //拿到Bitmap后直接显示在Image控件上
                imageView.setImageBitmap(photo);

                //将图片上传到服务器
//                String fileName = CommonCacheUtil.getUserId();
//                final File file = FileUtil.saveImgFile(this, photo, fileName);
//                final String fileKey = UUID.randomUUID().toString().replaceAll("-", "");
                //将file通过post上传到服务器
                //TODO:后续自行发挥
            }
        }
    }
}

3、FileUtil 工具类:

public class FileUtil {
    public static Uri getUri(Context context, String authority, File file) {
        Uri uri = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            uri = FileProvider.getUriForFile(context, authority, file);
        } else {
            uri = Uri.fromFile(file);
        }
        return uri;
    }

    public static File saveImgFile(Context context, Bitmap bitmap, String fileName) {
        if (fileName == null) {
            System.out.println("saved fileName can not be null");
            return null;
        } else {
            fileName = fileName + ".png";
            String path = context.getFilesDir().getAbsolutePath();
            String lastFilePath = path + "/" + fileName;
            File file = new File(lastFilePath);
            if (file.exists()) {
                file.delete();
            }

            try {
                FileOutputStream outputStream = context.openFileOutput(fileName, 0);
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                outputStream.flush();
                outputStream.close();
            } catch (FileNotFoundException var7) {
                var7.printStackTrace();
            } catch (IOException var8) {
                var8.printStackTrace();
            }

            return file;
        }
    }
}

4、工具类 CoreConstants:

public class CoreConstants {
    public static String getNurseDownloadFile(Context context) {
        return context.getExternalFilesDir("").getAbsolutePath() + "/img";
    }
}

5、在 AndroidManifest.xml 中配置 FileProvider:

 <application
     ....
     >
      <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true" >
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>
 </application>

6、filepaths.xml 文件:

<paths>
    <external-path path="notePadRecorder/" name="notePadRecorder" />
    <external-path name="my_images" path="Pictures"/>
    <external-path name="external_files" path="."/>
    <root-path name="root_path" path="." />
</paths>

这部分代码在小米和vivo手机上测过,是正常的。

目前线上也没有反馈在其他机型上该功能有问题,如有问题,后续持续更新此文章。


http://www.kler.cn/a/371070.html

相关文章:

  • C语言笔记(指针题目)例题+图解
  • GraphQL系列 - 第1讲 GraphQL语法入门
  • WUP-MY-POS-PRINTER 旻佑热敏打印机票据打印uniapp插件使用说明
  • Educational Codeforces Round 88 E. Modular Stability
  • 代码学习:如何阅读开源代码
  • 深入解析HTTP与HTTPS的区别及实现原理
  • java-web-day5
  • 科东软件荣获2024年广州科技创新创业大赛轨道交通行业赛“创新突围奖”
  • 有符号除法 简单的带小数计算及权重约束_2024年10月7日
  • Java Lock CountDownLatch 总结
  • 机器人转人工时,开启实时质检(mod_cti基于FreeSWITCH)
  • 计算机网络IP地址分类,子网掩码,子网划分复习资料
  • kafka 的高可用机制是什么?
  • zabbix 6.0 监控clickhouse(单机)
  • Spring 启动流程分析
  • 橘子多开同步器 v6.0 免费版
  • Redis-README官方入门文档
  • 【JSON相关漏洞(Hijacking+Injection)挖掘技巧及实战案例全汇总】
  • 基于Qt的多线程并行和循序运行实验Demo
  • yolov8环境搭建+训练自己的数据集
  • Notepad++如何同时检索多个关键字
  • 单目相机标定
  • PG数据库之视图详解
  • React写关键字高亮的三个方案
  • Node.js:内置模块
  • Docker | 通过commit操作实例来认识镜像底层实现的原理以及学会打包镜像