Android调用系统相机/相册并裁剪详解

Posted 我想月薪过万

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android调用系统相机/相册并裁剪详解相关的知识,希望对你有一定的参考价值。

效果展示

源码下载

android调用系统相机/相册并裁剪源码-互联网文档类资源-CSDN文库https://download.csdn.net/download/qq_41885673/34059233

关键技术

      android文件管理系统

代码编写

第一步:布局文件的准备

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

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="相机"
        android:onClick="camera"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="相册"
        android:onClick="album"/>

    <ImageView
        android:id="@+id/iv_show_select"
        android:layout_width="match_parent"
        android:layout_height="400dp"/>

</LinearLayout>

第二步:相机 拍照 然后获取照片并显示

  • 方法一:调用系统相机时未设置 uri
package com.wust.camerademo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;

import java.io.FileNotFoundException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    private ImageView iv_show_select;
    private int CAMERA_REQ_CODE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_show_select = findViewById(R.id.iv_show_select);
    }

    public void camera(View v) {
        //跳转时未设置 uri
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, CAMERA_REQ_CODE);
    }

    public void album(View v) {

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_OK) {
            if (data.hasExtra("data")) {
                // 方法一:没有指定 uri 时图片资源由 data.getParcelableExtra("data") 获取
                iv_show_select.setImageBitmap(data.getParcelableExtra("data"));
                System.out.println("拍照完成");
            }
        } else if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_CANCELED) {
            System.out.println("拍照取消");
        }
    }
}
  • 方法二:调用系统相机时设置了 uri

注意点一:当 android api 为 23 的时候 这个 uri 已经不能简单的由  Uri.fromFile(file) 获取了,否则会报错,如下所示:

Caused by: android.os.FileUriExposedException: file:///data/user/0/com.wust.camerademo/files/test.jpg exposed beyond app through ClipData.Item.getUri()

注意点二: 我们得使用 FileProvider ,但是别忘了在清单文件中配置

Couldn‘t find meta-data for provider with authority com.wust.camerademo_我想月薪过万的博客-CSDN博客https://blog.csdn.net/qq_41885673/article/details/120925348注意点三:Android_照相机Camera_调用系统照相机返回data为空Android_照相机Camera_调用系统照相机返回data为空_zimo2013的技术博客-CSDN博客https://blog.csdn.net/zimo2013/article/details/16916279

正确完整代码

package com.wust.camerademo;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;

import android.content.Intent;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.View;
import android.widget.ImageView;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    private ImageView iv_show_select;
    private int CAMERA_REQ_CODE = 1;
    private Uri uri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        iv_show_select = findViewById(R.id.iv_show_select);
    }

    public void camera(View v) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        String path = getFilesDir().getAbsolutePath() + File.separator + "test.jpg"; //这里只是当前目录,如果是多级文件夹 得通过 mkdirs 创建
        File file = new File(path);

        //由于 Android 文件安全机制 向第三方应用提供路径的时候得使用 FileProvider,注意需要在清单文件中注册
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            uri = FileProvider.getUriForFile(MainActivity.this, "com.wust.camerademo", file);
        } else {
            uri = Uri.fromFile(file);
        }

        //这里设置了 uri 那么后面就不能使用 data.getParcelableExtra("data") 获取图片了
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

        startActivityForResult(intent, CAMERA_REQ_CODE);
    }

    public void album(View v) {

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_OK) {
            System.out.println(data);
            if (data != null && data.hasExtra("data")) {
                // 方法一:没有指定 uri 时图片资源由 data.getParcelableExtra("data") 获取
                iv_show_select.setImageBitmap(data.getParcelableExtra("data"));
                System.out.println("data.getParcelableExtra(data) 方式获取");
            } else {
                // 方法二:通过 uri 方式获取
                try {
                    InputStream is = getContentResolver().openInputStream(uri);
                    iv_show_select.setImageBitmap(BitmapFactory.decodeStream(is));
                    is.close();
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
                System.out.println("uri 方式获取 =》 " + uri);
            }
        } else if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_CANCELED) {
            System.out.println("拍照取消");
        }
    }
}

附件:

清单文件配置

<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.wust.camerademo"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
</provider>

file_paths文件配置

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path
        name="test"
        path="." />
</paths>

第三步:相册 获取 照片

public void album(View v) {
        //跳转相册 Intent.ACTION_PICK 、Intent.ACTION_GET_CONTENT 、Intent.ACTION_OPEN_DOCUMENT 三者任选其一
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        startActivityForResult(intent, ALBUM_REQ_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_OK) {
            ...
        } else if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_CANCELED) {
            System.out.println("拍照取消");
        } else if (requestCode == ALBUM_REQ_CODE && resultCode == RESULT_OK) {
            Uri uri = data.getData();
            try {
                InputStream is = getContentResolver().openInputStream(uri);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                iv_show_select.setImageBitmap(bitmap);
            } catch (Exception e) {

            }
            System.out.println("相册获取");
        }
    }

第四步:调用系统裁剪工具对图片进行裁剪

注意点一:报错如下

Caused by: java.lang.SecurityException: UID 10221 does not have permission to content://com.android.providers.media.documents/document/image%3A681 [user 0]; you could obtain access using ACTION_OPEN_DOCUMENT or related APIs

 错误原因:在进行 相册 跳转时 应该选择 

 Intent.ACTION_PICK 或 Intent.ACTION_OPEN_DOCUMENT

注意点二:如果报如下错误,说明创建的 uri 有问题,导致裁剪后的图片无法保存

注意点三:如下错误是由于android文件系统安全性限制导致的,修改的地方还是 uri 创建处。但是这里的修改要注意 不能使用 FileProvider ,因为这个导致裁剪后的图片无法保存;也不能使用传统的  Environment.getExternalStorageDirectory() 因为android Q 之后,外置存储环境也设置了分区,每个应用只能访问自己的分区(根据包名划分分区)。所以,综上所述,在创建 uri 的时候得使用   getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)

 

正确代码

跳转相册部分代码

public void album(View v) {
        //跳转相册 Intent.ACTION_PICK 、Intent.ACTION_GET_CONTENT 、Intent.ACTION_OPEN_DOCUMENT 三者任选其一
        Intent intent = new Intent(Intent.ACTION_PICK); //这个地方会导致 报 注意一 的错误
        intent.setType("image/*");
        startActivityForResult(intent, ALBUM_REQ_CODE);
}

通过相册选取照片后的代码

 @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_OK) {
            ...
        } else if (requestCode == CAMERA_REQ_CODE && resultCode == RESULT_CANCELED) {
            System.out.println("拍照取消");
        } else if (requestCode == ALBUM_REQ_CODE && resultCode == RESULT_OK) {
            Uri uri = data.getData();

            cropPhoto(uri);

            System.out.println("相册获取");
        } else if (requestCode == CROP_REQ_CODE && resultCode == RESULT_OK) {
            try {
                InputStream is = getContentResolver().openInputStream(uritempFile);
                Bitmap bitmap = BitmapFactory.decodeStream(is);
                iv_show_select.setImageBitmap(bitmap);
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("图片裁剪完毕");
        }
    }

裁剪照片的代码 

private void cropPhoto(Uri uri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        //添加读写权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        //设置裁剪数据来源和类型
        intent.setDataAndType(uri, "image/*");
        //设置裁剪为true
        intent.putExtra("crop", "true");
        //设置大小
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 300);
        intent.putExtra("outputY", 300);
        //不能以这种形式返回图片数据 因为现在图片很大 我们得以 uri 方式返回
        //intent.putExtra("return-data", true); 配合 onActivityResult
        // =》 Bundle bundle = intent.getExtras();
        // =》 final Bitmap image = bundle.getParcelable("data");
        //创建 uri getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) 这个地方会出现 注意点二 或 注意点三 的报错
        uritempFile = Uri.parse("file://" + "/" + getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS) + "/" + "small.jpg");
        System.out.println("uritempFile => " + uritempFile);
        //设置 uri
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
        //设置格式
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        startActivityForResult(intent, CROP_REQ_CODE);
    }

源码下载

Android调用系统相机/相册并裁剪源码-互联网文档类资源-CSDN文库https://download.csdn.net/download/qq_41885673/34059233 

以上是关于Android调用系统相机/相册并裁剪详解的主要内容,如果未能解决你的问题,请参考以下文章

android实现头像更改并保存(调用系统相册,系统相机)

调用系统的相机和图库,并裁剪

Android Studio 调用系统相机(超清)和相册的照片并显示在ImageView

Android中通过访问本地相册或者相机设置用户头像

Android--利用相机或相册截取用户头像(解决了miui无法截取,以及部分机型拍照无返回Uri)

系统相机 系统相册 裁剪示例