0
点赞
收藏
分享

微信扫一扫

Adroid11,拍照,裁剪以及保存图片

Mhhao 2022-04-13 阅读 68

目录

1. 文件权限问题

2. 拍照权限和布局文件

3. 一些用的上的工具函数

4. 结果展示


完成毕业设计的需要,Android11要求实现图片拍照并裁剪,例如头像的选取。真机redmi k20;Android studio2021.1.1 java编程。

1. 文件权限问题

这是Android11在拍照裁剪最大的问题。Android11对app存储权限设置了分区,app默认只能访问自己app内的文件,而公共区域的文件则需要添加provider来支持访问,而其他app内部的文件访问权限则需要目的app开启权限,一般情况下都无法访问其他app的私有文件。

我当前实现的拍照及裁剪都是Android系统自带的功能,利用intent调用。因为两个功能都是公共的存储区域文件,则需要添加provider,在清单文件中添加如下代码到application标签中。

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="自己的包名"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

 这里要求有一个file_paths的xml文件,可以使用报红提示自动生成文件,或者在res文件夹下新建xml文件夹,新建一个xml文件

添加代码:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <paths>
        <root-path
            name="root"
            path="" />
        <files-path
            name="files"
            path="" />

        <cache-path
            name="cache"
            path="" />

        <external-path
            name="external"
            path="" />

        <external-files-path
            name="external_file_path"
            path="" />
        <external-cache-path
            name="external_cache_path"
            path="" />
    </paths>
</resources>

2. 拍照权限和布局文件

在清单文件中添加以下权限:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.CAMERA"/>

第一天读取外部存储,第二条相机权限。

Android11自然也是需要动态获取拍照的权限,代码:

    private void permission() {
        int REQUEST_CODE_CONTACT = 101;
        String[] strPermissions = {
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA};
        for (String strPermission : strPermissions) {
            if (checkSelfPermission(strPermission) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(strPermissions, REQUEST_CODE_CONTACT);
            }
        }
    }

记得调用该函数。读写权限都要。

布局文件很简单,一个按钮一个Imageview就行了。

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

    <Button
        android:id="@+id/pick"
        android:layout_width="160dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="选择图片"
        android:textSize="20sp" />

    <ImageView
        android:layout_gravity="center"
        android:id="@+id/image_view"
        android:layout_width="400dp"
        android:layout_height="400dp"/>

</LinearLayout>

全局变量://根据自己喜好设置,RESULT_OK不能改

​
    private static final int CAMERA_CODE = 20;
    private static final int CROP_CODE = 19;
    private static final int RESULT_OK = -1;

    String path;
    Uri uri;

​

绑定控件:

​
    private void init() {
        picture = findViewById(R.id.image_view);
        picker = findViewById(R.id.pick);
        //自定义的图片保存路径
        path = getFilesDir().getPath() + "/icon.jpeg";
        //uri用以存放android裁剪后自动保存的路径
        //保存在公共区域
        File uriFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        //UriUtils是关于路径的操作
        uri = UriUtils.getUriForFile(main, uriFile);
    }

​

点击函数中内容://暂时不想写相册获取,原理都差不多

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
            startActivityForResult(intent, CAMERA_CODE);

直接利用Intent跳转到拍照界面,然后在onActivityResult函数中做结果处理代码如下:

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Intent intent;
        if (requestCode == CAMERA_CODE) {
            mDialog.dismiss();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                //PhotoUtils存放关于照片的一些操作
                intent = PhotoUtils.startPhotoZoom(uri, uri, 2000, 2000);
            } else {
                intent = PhotoUtils.startPhotoZoom(uri, Uri.parse(path), 2000, 2000);
            }
            startActivityForResult(intent, CROP_CODE);
        }
        if (requestCode == CROP_CODE) {
            new Thread(() -> {
                String outputPath = UriUtils.getFileAbsolutePath(this, uri);
                System.out.println(outputPath);
            }).start();
            bitmap = decodeUriAsBitmap(uri);
            picture.setImageBitmap(bitmap);
            PhotoUtils.save(bitmap, new File(path), Bitmap.CompressFormat.JPEG, false);
        }

    }

在完成拍照并回调后直接利用Intent跳转到裁剪界面,裁剪完成后获取图片bitmap,并显示在Imageview空间上。

3. 一些用的上的工具函数

UriUtils.getUriForFile:

    /**
     * 获取uri
     *
     * @param context context
     * @param file 文件路径
     * @return
     */
    public static Uri getUriForFile(Context context, File file) {
        Uri fileUri = null;
        String filePath = file.getAbsolutePath();
        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[]{"_id"}, "_data=? ", new String[]{filePath}, null);
        if (cursor != null && cursor.moveToFirst()) {
            int id = cursor.getInt(cursor.getColumnIndex("_id"));
            Uri baseUri = Uri.parse("content://media/external/images/media");
            fileUri = Uri.withAppendedPath(baseUri, "" + id);
        } else if (file.exists()) {
            ContentValues values = new ContentValues();
            values.put("_data", filePath);
            fileUri = context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        } else {
            fileUri = null;
        }
        return fileUri;
    }

 PhotoUtils:


    /**
     * 调用系统裁剪<br>
     *
     * @param uri        需要裁剪的图片路径
     * @param mImagePath 图片输出路径
     * @param sizeX      裁剪x
     * @param sizeY      裁剪y
     */
    public static Intent startPhotoZoom(Uri uri, Uri mImagePath, int sizeX, int sizeY) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        //裁剪后输出路径
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mImagePath);
        //输入图片路径
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        intent.putExtra("circleCrop", "true");
        //华为的手机原型裁剪适应大小
        if (Build.MANUFACTURER.equals("HUAWEI")) {
            intent.putExtra("aspectX", 9998);
            intent.putExtra("aspectY", 9999);
        } else {
            intent.putExtra("aspectX", sizeX);
            intent.putExtra("aspectY", sizeY);
        }
        //输出的图片size越大越清晰,但是过大可能会导致程序崩溃
        intent.putExtra("outputX", sizeX);
        intent.putExtra("outputY", sizeY);
        intent.putExtra("scale", true);
        intent.putExtra("scaleUpIfNeeded", true);
        //设置不返回数据,而是返回JPEG格式,在函数中调用编码函数再显示,这样图片更清晰
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("return-data", false);

        return intent;
    }


    /**
     * 保存图片到文件File。
     *
     * @param src     源图片
     * @param file    要保存到的文件
     * @param format  格式
     * @param recycle 是否回收
     * @return true 成功 false 失败
     */
    public static boolean save(Bitmap src, File file, Bitmap.CompressFormat format, boolean recycle) {
        if (src == null)
            return false;

        OutputStream os;
        boolean ret = false;
        try {
            os = new BufferedOutputStream(new FileOutputStream(file));
            ret = src.compress(format, 100, os);
            if (recycle && !src.isRecycled())
                src.recycle();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return ret;
    }

decodeUriAsBitmap:


    private Bitmap decodeUriAsBitmap(Uri uri) {
        Bitmap bitmap = null;
        try {
            // 先通过getContentResolver方法获得一个ContentResolver实例,
            // 调用openInputStream(Uri)方法获得uri关联的数据流stream
            // 把上一步获得的数据流解析成为bitmap
            bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
        return bitmap;
    }

4. 结果展示

代码若有疑问欢迎批评

暂时还未上传代码到git,后续可能会添加。

举报

相关推荐

0 条评论