Android 打开相机、相册获取图片文件,适配支持Android 10.0、Android 9.0系统,解决解决Android 6.0以上的高版本跳转相机闪退问题,以及相机返回图片不显示问题。

Android 打开相机、相册获取图片文件,适配支持Android 10.0、Android 9.0系统,解决解决Android 6.0以上的高版本跳转相机闪退问题,以及相机返回图片不显示问题。

代码复制可用,开发工具为:Android Studio
嫌麻烦也可以直接下载源码
【注意:使用开发工具是 Android Studio 哦!】

文章看起来内容很多,其实只有很少的内容,为了让初学者能看懂,所以描述更详细一些。
在这里插入图片描述
先看看简洁工程结构:
在这里插入图片描述

AndroidManifest.xml 中添加权限

   <!--拍照-->
    <uses-permission android:name="android.permission.CAMERA" />
    <!--读写-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.PERMISSIONS_STORAGE"/>

添加 :android:requestLegacyExternalStorage="true"
<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.cameratest.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

如图所示:
在这里插入图片描述

创建配置文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <external-files-path path="/download" name="download"/>
    <root-path name="my_image" path="."/>
</resources>

如图所示:
在这里插入图片描述
在这里插入图片描述

详细完整代码:

创建一个类【CameraActivity.java】,然后把以下代码复制进去

package com.example.haoruidoctor.Activity;

import android.Manifest;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.util.Log;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.core.os.EnvironmentCompat;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * ERP_LXKUN_JAK on 2020/10/22
 * Current project HaoRuiDoctor
 * Annotations :相机相册裁剪图片操作
 */

public class CameraActivity extends AppCompatActivity {

    public static String TAG = "CameraActivity:";
    public static final String ExtraType = "CameraExtra";   //Activity传值key
    public static final int CAMERA = 99001;
    public static final int PHOTO = 99002;

    public static Uri IMG_URI = null;
    public static File IMG_File = null;
    public static boolean LISTENING = false;


    private static int aspectX = 0;  //设置裁剪区域的宽高比例
    private static int aspectY = 0;  //设置裁剪区域的宽高比例
    private static int outputX = 0; //设置裁剪区域的宽度和高度
    private static int outputY = 0; //设置裁剪区域的宽度和高度
    private static boolean scale = true;      //裁剪时是否可以缩放
    private static boolean noFaceDetection = false;  //是否检测人脸
    private static String outputFormat = Bitmap.CompressFormat.JPEG.toString();  //图片输出格式

    //设置图片输出格式
    public static void setOutputFormat(String outputFormats) {
        outputFormat = outputFormats;
    }

    //设置裁剪比例
    public static void setClipRatio(int aspectXs, int aspectYs) {
        aspectX = aspectXs;
        aspectY = aspectYs;
    }

    //设置裁剪像素
    public static void setClipPixel(int outputXs, int outputYs) {
        outputX = outputXs;
        outputY = outputYs;
    }

    //裁剪时是否可以缩放
    public static void setScales(boolean scales) {
        scale = scales;
    }

    //是否检测人脸
    public static void setNoFaceDetections(boolean noFaceDetections) {
        noFaceDetection = noFaceDetections;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        int CameraExtra = intent.getIntExtra(ExtraType, 0);

        //动态获取权限
        boolean WRITE2 = RequestPermissions(CameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        boolean READ2 = RequestPermissions(CameraActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE);
        boolean CAMERA2 = RequestPermissions(CameraActivity.this, Manifest.permission.CAMERA);

        if (WRITE2 && READ2 && CAMERA2) {
            switch (CameraExtra) {
                case CAMERA:
                    openCamera();  //相机操作
                    break;
                case PHOTO:
                    openGallery();  //相册操作
                    break;
            }
        } else {
            CameraActivity.this.finish();
        }
    }

    // 申请相机权限的requestCode
    private static final int PERMISSION_CAMERA_REQUEST_CODE = 0x00000012;
    private static final int REQUEST_TAKE_PHOTO = 0;// 拍照
    private static final int REQUEST_CROP = 1;// 裁剪
    private static final int SCAN_OPEN_PHONE = 2;// 相册
    private Uri mCameraUri;//拍照时返回的uri
    private String mCameraImagePath;// 用于保存图片的文件路径,Android 10以下使用图片路径访问图片
    private boolean isAndroidQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;// 是否是Android 10以上手机
    private Uri mCutUri;// 图片裁剪时返回的uri
    private File imgFile;// 拍照保存的图片文件
    private File mCutFile;

    //从相册获取图片
    private void openGallery() {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType("image/*");
        startActivityForResult(intent, SCAN_OPEN_PHONE);
    }

    /**
     * 处理权限申请的回调。
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSION_CAMERA_REQUEST_CODE) {
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //允许权限,有调起相机拍照。
                openCamera();
            } else {
                //拒绝权限,弹出提示框。
                Toast.makeText(this, "拍照权限被拒绝", Toast.LENGTH_LONG).show();
            }
        }
    }


    // 图片裁剪
    private void cropPhoto(Uri uri, boolean fromCapture) {
        Intent intent = new Intent("com.android.camera.action.CROP"); //打开系统自带的裁剪图片的intent
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");

        // 注意一定要添加该项权限,否则会提示无法裁剪
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

        intent.putExtra("scale", scale);

        if (aspectX != 0)
            intent.putExtra("aspectX", aspectX);
        if (aspectY != 0)
            intent.putExtra("aspectY", aspectY);
        if (outputX != 0)
            intent.putExtra("outputX", outputX);
        if (outputY != 0)
            intent.putExtra("outputY", outputY);
        // 取消人脸识别
        intent.putExtra("noFaceDetection", noFaceDetection);
        // 图片输出格式
        intent.putExtra("outputFormat", outputFormat);
        // 若为false则表示不返回数据
        intent.putExtra("return-data", false);

        // 指定裁剪完成以后的图片所保存的位置,pic info显示有延时
        if (fromCapture) {
            // 如果是使用拍照,那么原先的uri和最终目标的uri一致,注意这里的uri必须是Uri.fromFile生成的
            if (isAndroidQ) {
                // 适配android 10
                mCutUri = uri;
                mCutFile = uri2File(CameraActivity.this, mCutUri);
            } else {
                Log.e(TAG, "android 10 以下版本");
                mCutUri = Uri.fromFile(imgFile);
                mCutFile = imgFile;
            }

        } else { // 裁剪的图片保存在take_photo中
            String time = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA).format(new Date());
            String fileName = time;   //文件命名
            mCutFile = new File(Environment.getExternalStorageDirectory() + "/take_photo/", fileName + ".jpg");
            if (!mCutFile.getParentFile().exists()) {
                mCutFile.getParentFile().mkdirs();
            }
            mCutUri = Uri.fromFile(mCutFile);
        }
        intent.putExtra(MediaStore.EXTRA_OUTPUT, mCutUri);
        Toast.makeText(this, "剪裁图片", Toast.LENGTH_SHORT).show();
        // 以广播方式刷新系统相册,以便能够在相册中找到刚刚所拍摄和裁剪的照片
        Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        intentBc.setData(uri);
        this.sendBroadcast(intentBc);
        startActivityForResult(intent, REQUEST_CROP); //设置裁剪参数显示图片至ImageVie
    }

    /**
     * 调起相机拍照
     */
    private void openCamera() {
        Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 判断是否有相机
        if (captureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            Uri photoUri = null;

            if (isAndroidQ) {  // 适配android 10
                photoUri = createImageUri();
            } else {
                try {
                    photoFile = createImageFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (photoFile != null) {
                    mCameraImagePath = photoFile.getAbsolutePath();
                    imgFile = photoFile;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        //适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri
                        photoUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile);
                    } else {
                        photoUri = Uri.fromFile(photoFile);
                    }
                }
            }

            mCameraUri = photoUri;
            if (photoUri != null) {
                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                startActivityForResult(captureIntent, REQUEST_TAKE_PHOTO);
            }
        } else {
            Toast.makeText(CameraActivity.this, "当前系统没有可用相机", Toast.LENGTH_LONG).show();
            CameraActivity.this.finish();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                // 拍照并进行裁剪
                case REQUEST_TAKE_PHOTO:
                    Log.e(TAG, "onActivityResult: imgUri:REQUEST_TAKE_PHOTO:" + mCameraUri.toString());
                    cropPhoto(mCameraUri, true);
                    break;
                // 裁剪后设置图片
                case REQUEST_CROP:

                    File file = uriToFileApiQ(mCutUri);
                    IMG_URI = mCutUri;
                    IMG_File = file;

                    Log.i(TAG, "IMG_URI:" + IMG_URI);
                    Log.i(TAG, "IMG_File:" + IMG_File);
                    Log.i(TAG, "getPath:" + file.getPath());
                    Log.i(TAG, "getName:" + file.getName());

                    CameraActivity.LISTENING = true;
                    CameraActivity.this.finish();
                    break;
                // 打开图库获取图片并进行裁剪
                case SCAN_OPEN_PHONE:
                    Log.e(TAG, "onActivityResult: SCAN_OPEN_PHONE:" + data.getData().toString());
                    cropPhoto(data.getData(), false);
                    break;
            }
        } else {
            //Toast.makeText(this, "取消", Toast.LENGTH_LONG).show();
            CameraActivity.this.finish();
        }
    }

    /**
     * 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
     */
    private Uri createImageUri() {
        String status = Environment.getExternalStorageState();
        // 判断是否有SD卡,优先使用SD卡存储,当没有SD卡时使用手机存储
        if (status.equals(Environment.MEDIA_MOUNTED)) {
            return getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
        } else {
            return getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
        }
    }

    /**
     * 创建保存图片的文件
     */
    private File createImageFile() throws IOException {
        String imageName = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date());
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        if (!storageDir.exists()) {
            storageDir.mkdir();
        }
        File tempFile = new File(storageDir, imageName + ".jpg");
        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
            return null;
        }
        return tempFile;
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public File uriToFileApiQ(Uri uri) {
        File file = null;
        //android10以上转换
        if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
            file = new File(uri.getPath());
        } else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
            //把文件复制到沙盒目录
            ContentResolver contentResolver = CameraActivity.this.getContentResolver();
            Cursor cursor = contentResolver.query(uri, null, null, null, null);
            if (cursor.moveToFirst()) {
                String displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                try {
                    InputStream is = contentResolver.openInputStream(uri);
                    File cache = new File(CameraActivity.this.getExternalCacheDir().getAbsolutePath(), Math.round((Math.random() + 1) * 1000) + displayName);
                    FileOutputStream fos = new FileOutputStream(cache);
                    FileUtils.copy(is, fos);
                    file = cache;
                    fos.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return file;
    }


    /**
     * 动态申请权限
     *
     * @param context    上下文
     * @param permission 要申请的一个权限,列如写的权限:Manifest.permission.WRITE_EXTERNAL_STORAGE
     * @return 是否有当前权限
     */
    private boolean RequestPermissions(@NonNull Context context, @NonNull String permission) {
        if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
            Log.i("requestMyPermissions", ": 【 " + permission + " 】没有授权,申请权限");
            ActivityCompat.requestPermissions((Activity) context, new String[]{permission}, 100);
            return false;
        } else {
            Log.i("requestMyPermissions", ": 【 " + permission + " 】有权限");
            return true;
        }
    }

    /**
     * user转换为file文件
     * 返回值为file类型
     *
     * @param uri
     * @return
     */
    private File uri2File(Activity activity, Uri uri) {
        String img_path;
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor actualimagecursor = activity.managedQuery(uri, proj, null, null, null);
        if (actualimagecursor == null) {
            img_path = uri.getPath();
        } else {
            int actual_image_column_index = actualimagecursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            actualimagecursor.moveToFirst();
            img_path = actualimagecursor.getString(actual_image_column_index);
        }
        File file = new File(img_path + ".jpg");
        return file;
    }

}

在主页面中非常简单的调用操作即可【MainActivity .java】

public class MainActivity extends AppCompatActivity {

    private ImageView IMG;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IMG = findViewById(R.id.IMG);
        //设置裁剪比例
        CameraActivity.setClipRatio(1, 1);

        // setOutputFormat()  设置图片输出格式
        // setClipRatio()  设置裁剪比例
        // setClipPixel()  设置裁剪像素
        // setScales()  裁剪时是否可以缩放
        // setNoFaceDetections()  是否检测人脸
    }

    //从相机获取图片
    public void TestBtn01(View view) {
        startActivity(new Intent(MainActivity.this, CameraActivity.class).putExtra(CameraActivity.ExtraType, CameraActivity.CAMERA));
    }

    //从相册获取图片
    public void TestBtn02(View view) {
        startActivity(new Intent(MainActivity.this, CameraActivity.class).putExtra(CameraActivity.ExtraType, CameraActivity.PHOTO));
    }

    @Override
    protected void onResume() {
        super.onResume();
        //获得相册、相机返回的结果,并显示
        if (CameraActivity.LISTENING) {
            Log.e("TAG", "返回的Uri结果:" + CameraActivity.IMG_URI);
            Log.e("TAG", "返回的File结果:" + CameraActivity.IMG_File.getPath());
            CameraActivity.LISTENING = false;   //关闭获取结果
            IMG.setImageURI(CameraActivity.IMG_URI);  //显示图片到控件
        }
    }
}

【布局文件:activity_main.xml】

<?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:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ImageView
        android:id="@+id/IMG"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_margin="20dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_weight="1"
            android:background="#BFBFBF"
            android:gravity="center"
            android:onClick="TestBtn01"
            android:text="从相机获取图片"
            android:textColor="#090909" />

        <TextView
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_weight="1"
            android:background="#BFBFBF"
            android:gravity="center"
            android:onClick="TestBtn02"
            android:text="从相册获取图片"
            android:textColor="#090909" />
    </LinearLayout>

</LinearLayout>

.

源码下载
【注意:使用开发工具是 Android Studio 哦!】
.
感谢你的查阅,希望可以帮到你,祝你学习愉快!
.

我是和你一起学习的 易君

  • 28
    点赞
  • 92
    收藏
    觉得还不错? 一键收藏
  • 47
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值