Android10及以下版本调用系统相机拍照并展示

一、拍照

1.首先看build.gradle里的配置

compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.donkingliang.photograph"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

目标版本为29,即Android10;

2.先看一段关键代码:

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();
                    System.out.println("path = "+mCameraImagePath);
                    if (/*Build.VERSION.SDK_INT >= Build.VERSION_CODES.N*/true) {//7.0到9.0
                        //适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri 如:content://
                        photoUri = FileProvider.getUriForFile(MainActivity.this,
                                "com.donkingliang.photograph.fileprovider", photoFile);
                    } else {//7.0以下, 如:file://
                        photoUri = Uri.fromFile(photoFile);
                    }
                }
            }

            System.out.println("photoUri = "+photoUri);
            mCameraUri = photoUri;
            if (photoUri != null) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    List<ResolveInfo> resInfoList = getPackageManager()
                            .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                    for (ResolveInfo resolveInfo : resInfoList) {
                        String packageName = resolveInfo.activityInfo.packageName;
                        grantUriPermission(packageName, photoUri,
                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }
                }

                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);
            }
        }
    }

/**
     * 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
     * @return 图片的uri
     */
    private Uri createImageUri() {
        //设置保存参数到ContentValues中
        ContentValues contentValues = new ContentValues();
        //设置文件名
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis()+"");
        //兼容Android Q和以下版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            //android Q中不再使用DATA字段,而用RELATIVE_PATH代替
            //TODO RELATIVE_PATH是相对路径不是绝对路径;照片存储的地方为:内部存储/Pictures/preventpro
            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/preventpro");
        }
        //设置文件类型
        contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG");
        //执行insert操作,向系统文件夹中添加文件
        //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        return uri;

    }

    /**
     * 创建保存图片的文件
     * @return
     * @throws IOException
     */
    private File createImageFile() throws IOException {
        String imageName = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date()) +".jpg";
//        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//        File storageDir = Environment.getExternalStoragePublicDirectory(
//                Environment.DIRECTORY_PICTURES);
        File storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                +File.separator+"Pictures"+File.separator+"abc");
        if (!storageDir.exists()) storageDir.mkdirs();

        File tempFile = new File(storageDir, imageName);
        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
            return null;
        }
        return tempFile;
    }

通过隐士的方式调用系统相机;关键在于指定好图片的uri,然后通过intent的captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);来跳转到系统相机界面;

3.Android6.0及以下怎么获取uri:  格式为 file://

photoUri = Uri.fromFile(photoFile);

只需这句话即可;

4.Android7.0到Android9.0

photoUri = FileProvider.getUriForFile(MainActivity.this,
                                "com.donkingliang.photograph.fileprovider", photoFile);

第一个参数是上下文;第三个参数是图片的file对象;第二个参数是签名,要和AndroidManifest.xml文件中的写法一致:

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

android:authorities就是签名,一般就是包名+provider,根据自己需要来写吧,没啥要求;可以看到下面的android:resource="@xml/file_paths"是个资源文件,它在res目录下,自己建一个xml文件夹,新建一个file_paths.xml文件;

<?xml version="1.0" encoding="utf-8"?>
    <paths>
<!--        该方式提供在应用的内部存储区的文件/子目录的文件。它对应Context.getFilesDir返回的路径:-->
<!--        eg:"/data/data/com.jph.simple/files"。-->
<!--        <files-path path="" name="camera_photos" />-->

<!--        该方式提供在应用的内部存储区的缓存子目录的文件。它对应getCacheDir返回的路径:-->
<!--        eg:“/data/data/com.jph.simple/cache”;-->
<!--        <cache-path name="name" path="path" />-->


<!--        该方式提供在外部存储区域根目录下的文件。它对应Environment.getExternalStorageDirectory返回的路径:-->
<!--        eg:"/storage/emulated/0";-->
        <external-path name="my_images" path="Pictures" />

        <!-- 这个是保存拍照图片的路径,必须配置。 -->
<!--        该方式提供在应用的外部存储区根目录的下的文件。它对应Context#getExternalFilesDir(String) -->
<!--        Context.getExternalFilesDir(null)返回的路径:-->
<!--        eg:"/storage/emulated/0/Android/data/com.jph.simple/files"。-->
        <external-files-path name="images" path="Pictures" />

<!--        该方式提供在应用的外部缓存区根目录的文件。它对应Context.getExternalCacheDir()返回的路径。-->
<!--        eg:"/storage/emulated/0/Android/data/com.jph.simple/cache"。-->
<!--        <external-cache-path name="name" path="path" />-->


<!--        以上便是Android官方文档上介绍的FileProvider所有支持的所以path类型,这些类型在Android手机内部存储区-->
<!--        文件共享是可以行的通的,但对于外置SD卡是不行的,如果你想通过FileProvider.getUriForFile()获取一个-->
<!--        外置SD卡的Uri则会报出如下异常:-->
<!--        Caused by- java.lang.IllegalArgumentException- Failed to find configured root that contains-->
        <root-path path="." name="root_path" />
    </paths>

这里面有不同的标签,可以根据自己需要去设置;具体就读一下注释吧,很好理解。

利用FileProvider来获取uri的方法在6.0上同样适用,所以上面的openCamera方法中我把判断条件注释了直接写为true;开发中直接写即可。

5.Android10获取uri

使用方法createImageUri来做的,androidQ中不再使用DATA字段,而用RELATIVE_PATH代替;ContentResolver().insert来得到uri;具体看方法中的注释。里面可以自定义图片名字,存放的地址,图片类型等等;这里存到了 根目录/Pictures/preventpro 下。

6.拍照完成后的回调

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        System.out.println("==================resultCode = "+resultCode);
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                ivPhoto.setImageURI(mCameraUri);
            } else {
                Toast.makeText(this,"取消",Toast.LENGTH_LONG).show();
            }
        }
    }

直接通过拍照时获取的uri来填充控件显示图片。同时拍的照片也在系统的相册和文件管理中都可看到。

7.下面是完整的代码:

public class MainActivity extends AppCompatActivity {

    private ImageView ivCamera;
    private ImageView ivPhoto;

    // 拍照的requestCode
    private static final int CAMERA_REQUEST_CODE = 0x00000010;
    // 申请相机权限的requestCode
    private static final int PERMISSION_CAMERA_REQUEST_CODE = 0x00000012;
    /**
     * 用于保存拍照图片的uri
      */
    private Uri mCameraUri;

    /**
     * 用于保存图片的文件路径,Android 10以下使用图片路径访问图片
     */
    private String mCameraImagePath;

    /**
     *  是否是Android 10以上手机
      */
    private boolean isAndroidQ = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;

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

        ivCamera = findViewById(R.id.ivCamera);
        ivPhoto = findViewById(R.id.ivPhoto);

        ivCamera.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkPermissionAndCamera();
            }
        });
    }

    /**
     * 检查权限并拍照。
     * 调用相机前先检查权限。
     */
    private void checkPermissionAndCamera() {
        int hasCameraPermission = ContextCompat.checkSelfPermission(getApplication(),
                Manifest.permission.CAMERA);
        if (hasCameraPermission == PackageManager.PERMISSION_GRANTED) {
            //有权限,调起相机拍照。
            openCamera();
        } else {
            //没有权限,申请权限。
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},
                    PERMISSION_CAMERA_REQUEST_CODE);
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    PERMISSION_CAMERA_REQUEST_CODE);
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        System.out.println("==================resultCode = "+resultCode);
        if (requestCode == CAMERA_REQUEST_CODE) {
            if (resultCode == RESULT_OK) {
                ivPhoto.setImageURI(mCameraUri);
            } else {
                Toast.makeText(this,"取消",Toast.LENGTH_LONG).show();
            }
        }
    }

    /**
     * 处理权限申请的回调。
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @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 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();
                    System.out.println("path = "+mCameraImagePath);
                    if (/*Build.VERSION.SDK_INT >= Build.VERSION_CODES.N*/true) {//7.0到9.0
                        //适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri 如:content://
                        photoUri = FileProvider.getUriForFile(MainActivity.this,
                                "com.donkingliang.photograph.fileprovider", photoFile);
                    } else {//7.0以下, 如:file://
                        photoUri = Uri.fromFile(photoFile);
                    }
                }
            }

            System.out.println("photoUri = "+photoUri);
            mCameraUri = photoUri;
            if (photoUri != null) {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    List<ResolveInfo> resInfoList = getPackageManager()
                            .queryIntentActivities(captureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                    for (ResolveInfo resolveInfo : resInfoList) {
                        String packageName = resolveInfo.activityInfo.packageName;
                        grantUriPermission(packageName, photoUri,
                                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }
                }

                captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
                captureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                startActivityForResult(captureIntent, CAMERA_REQUEST_CODE);
            }
        }
    }

    /**
     * 创建图片地址uri,用于保存拍照后的照片 Android 10以后使用这种方法
     * @return 图片的uri
     */
    private Uri createImageUri() {
        //设置保存参数到ContentValues中
        ContentValues contentValues = new ContentValues();
        //设置文件名
        contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, System.currentTimeMillis()+"");
        //兼容Android Q和以下版本
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            //android Q中不再使用DATA字段,而用RELATIVE_PATH代替
            //TODO RELATIVE_PATH是相对路径不是绝对路径;照片存储的地方为:内部存储/Pictures/preventpro
            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/preventpro");
        }
        //设置文件类型
        contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/JPEG");
        //执行insert操作,向系统文件夹中添加文件
        //EXTERNAL_CONTENT_URI代表外部存储器,该值不变
        Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
        return uri;

    }

    /**
     * 创建保存图片的文件
     * @return
     * @throws IOException
     */
    private File createImageFile() throws IOException {
        String imageName = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date()) +".jpg";
//        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
//        File storageDir = Environment.getExternalStoragePublicDirectory(
//                Environment.DIRECTORY_PICTURES);
        File storageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                +File.separator+"Pictures"+File.separator+"abc");
        if (!storageDir.exists()) storageDir.mkdirs();

        File tempFile = new File(storageDir, imageName);
        if (!Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(tempFile))) {
            return null;
        }
        return tempFile;
    }
}


二、显示图库照片

1.因为Android10中废除了通过文件路径的读取图片,所以只能使用uri来读取图片;

先看一个方法:

//在公共文件夹下查询图片
    //这里的filepath在androidQ中表示相对路径
    //在androidQ以下是绝对路径
    public static Map<String, List<String>> querySignImage(String filePath, Context context, int typeShow) {
        // 图片列表
        Map<String, List<String>> dayMap = new HashMap<>();
        try {
            //兼容androidQ和以下版本
            String queryPathKey = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q ?
                    MediaStore.Images.Media.RELATIVE_PATH : MediaStore.Images.Media.DATA;
            //查询的条件语句
            String selection = queryPathKey + "=? ";
            //查询的sql
            //Uri:指向外部存储Uri
            //projection:查询那些结果
            //selection:查询的where条件
            //sortOrder:排序
            Cursor cursor = context.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    new String[]{MediaStore.Images.Media._ID,
                            queryPathKey,
                            MediaStore.Images.Media.MIME_TYPE,
                            MediaStore.Images.Media.DISPLAY_NAME},
                    selection,
                    new String[]{filePath},
                    null);
            //是否查询到了
            if (cursor != null && cursor.moveToFirst()) {
                //循环取出所有查询到的数据
                do {
                    //一张图片的基本信息
                    int id = cursor.getInt(cursor.getColumnIndex(MediaStore.Images.Media._ID));//uri的id,用于获取图片
                    String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.RELATIVE_PATH));//图片的相对路径
                    String type = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.MIME_TYPE));//图片类型
                    String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));//图片名字
                    System.out.println("id="+id+", path="+path+", type="+type+", name="+name);
                    //根据图片id获取uri,这里的操作是拼接uri
                    Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id);
                    long value = Long.valueOf(name.replace(".jpg", ""));
                    String dayPic = null;
                    switch (typeShow) {
                        case DAY:
                            dayPic = getDayPic(value);
                            break;
                        case WEEK:
                            dayPic = getWeekPic(value);
                            break;
                        case MONTH:
                            dayPic = getMonthPic(value);
                            break;
                        default:
                            break;
                    }

                    if (dayMap.containsKey(dayPic)) {
                        Objects.requireNonNull(dayMap.get(dayPic)).add(uri.toString());
                    }else {
                        List<String> imagePathList = new ArrayList<>();
                        imagePathList.add(uri.toString());
                        dayMap.put(dayPic, imagePathList);
                    }

                    //官方代码:
//                    Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
//                    if (uri != null) {
//                        //通过流转化成bitmap对象
//                        InputStream inputStream = context.getContentResolver().openInputStream(uri);
//                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
//                    }
                } while (cursor.moveToNext());
            }
            if (cursor != null)
                cursor.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dayMap;
    }

这是在公共文件夹下查找图片的方法;使用ContentResolver来查询图片;条件里面适配Android10和以下版本;查到的是传入的路径下的图片,Android10传入的相对路径,就是上面拍照时传进去的路径;其它版本传的是绝对路径;查到的结果如:

id=377383, path=Pictures/preventpro/, type=image/jpeg, name=1577084770593.jpg

我们知道了id,path等信息,就可以获取uri了:

//根据图片id获取uri,这里的操作是拼接uri
Uri uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id);

有了uri就可展示图片了。

2.其它版本还是可以使用绝对路径来获取图片并展示。这里不作展示了。

3.删除图片

if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
                context.getContentResolver().delete(Uri.parse(path), null, null);
            }else {
                File file = new File(path);
                if(file.exists()){
                    deleteImage(context, path);
                }
            }

public static boolean deleteImage(Context context, String imgPath) {
        ContentResolver resolver = context.getContentResolver();
        Cursor cursor = MediaStore.Images.Media.query(resolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=?",
                new String[] { imgPath }, null);
        boolean result = false;
        if (null != cursor && cursor.moveToFirst()) {
            long id = cursor.getLong(0);
            Uri contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            Uri uri = ContentUris.withAppendedId(contentUri, id);
            int count = context.getContentResolver().delete(uri, null, null);
            result = count == 1;
        } else {
            File file = new File(imgPath);
            result = file.delete();
        }
        return result;
    }

AndroidQ时,传过来的都是uri,如:content://media/external/images/media/377490   所以直接使用ContentResolver().delete;

而Android9及以下版本,我用的是path路径,所以就需要先通过ContentResolver,路径,Cursor查询到id,然后构建uri,再去删除了。ContentUris.withAppendedId(contentUri, id)是Android自带的拼接uri方法和Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + id);效果一样。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值