Android 调用系统相册、系统相机拍照

Android 调用系统相册、系统相机拍照工具类

第一步(准备工作):设置文件共享

1.1、指定 FileProvider

新建FileProvider类,名字随意,继承自FileProvider

java 复制代码
public class MainFileProvider extends FileProvider {
}

1.2 、清单中添加FileProvider、对应权限

修改AndroidManifest.xml文件

java 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp">
    
    <!--如果有多个摄像头默认使用后置摄像头-->
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false" />
        
    <uses-permission android:name="android.permission.CAMERA" />
    <!--Android10以下申请这个-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--Android11(含)以上申请这个-->
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    
    <application
        ...>
		<provider
            android:name=".MainFileProvider"
            android:authorities="${applicationId}.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>
        ...
    </application>
</manifest> 

1.3、指定可共享的目录

创建xml文件放于路径res/xml目录下,没有xm文件夹就手动创建

provider_paths.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<paths>
    <!--name: 名称标志字符串,不可以同名!-->
    <!--path: 文件夹"相对路径",完整路径取决于当前的标签类型。传空,代表你整个对应路径都可以用于共享-->

    <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_files"
        path="." />
    <!-- 此标签需要 support-v4:25.0.0以上才可以使用-->
    <external-cache-path
        name="external_cache"
        path="." />

</paths>

第二步(工具类):

1.1、工具类代码

PhotoImagePicker.java

java 复制代码
/**
 * 调用系统拍照、系统相册
 * 使用前请自行进行权限申请
 * 拍照图片会保存在/storage/emulated/0/Android/data/<应用包名>/files/Pictures
 */
public class PhotoImagePicker {
    private static volatile PhotoImagePicker instance = null;
    private PhotoPickCallback callback;
    private static final int PICK_IMAGE_CHOOSER_REQUEST_CODE = 200;
    private Uri outputFileUri;//拍照输出的uri

    public static PhotoImagePicker getInstance() {
        if (instance == null) {
            synchronized (PhotoImagePicker.class) {
                if (instance == null)
                    instance = new PhotoImagePicker();
            }
        }
        return instance;
    }

    /**
     * 启动照相机(Activity)
     */
    public void startCamera(Activity activity, PhotoPickCallback callback) {
        this.callback = callback;
        outputFileUri = Uri.fromFile(new File(activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES), System.currentTimeMillis() + ".jpg"));
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, getIntentUri(activity, outputFileUri));
        activity.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 启动照相机(Fragment)
     */
    public void startCamera(Fragment fragment, PhotoPickCallback callback) {
        this.callback = callback;
        outputFileUri = Uri.fromFile(new File(fragment.requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), System.currentTimeMillis() + ".jpg"));
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, getIntentUri(fragment.requireContext(), outputFileUri));
        fragment.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 启动图库选择器(Activity)
     */
    public void startGallery(Activity activity, PhotoPickCallback callback) {
        this.callback = callback;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        activity.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 启动图库选择器(Fragment)
     */
    public void startGallery(Fragment fragment, PhotoPickCallback callback) {
        this.callback = callback;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("image/*");
        fragment.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 启动文件选择器(Activity)
     */
    public void startChooser(Activity activity, String mime, PhotoPickCallback callback) {
        this.callback = callback;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(mime);
        activity.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 启动文件选择器(Fragment)
     */
    public void startChooser(Fragment fragment, String mime, PhotoPickCallback callback) {
        this.callback = callback;
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType(mime);
        fragment.startActivityForResult(intent, PICK_IMAGE_CHOOSER_REQUEST_CODE);
    }

    /**
     * 图片选择结果回调,在 {@link Activity#onActivityResult(int, int, Intent)} 中调用
     */
    @SuppressWarnings("JavadocReference")
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        onActivityResultInner(activity, null, requestCode, resultCode, data);
    }

    /**
     * 图片选择结果回调,在 {@link Fragment#onActivityResult(int, int, Intent)} 中调用
     */
    public void onActivityResult(Fragment fragment, int requestCode, int resultCode, Intent data) {
        onActivityResultInner(null, fragment, requestCode, resultCode, data);
    }

    private void onActivityResultInner(Activity activity, Fragment fragment, int requestCode, int resultCode, Intent data) {
        if (resultCode != Activity.RESULT_OK) {
            if (callback != null)
                callback.onCanceled();
            return;
        }
        Context context;
        if (activity != null) {
            context = activity;
        } else
            context = fragment.getContext();
        if (context != null && requestCode == PICK_IMAGE_CHOOSER_REQUEST_CODE) {
            boolean isCamera = true;
            if (data != null && data.getData() != null) {
                String action = data.getAction();
                isCamera = action != null && action.equals(MediaStore.ACTION_IMAGE_CAPTURE);
            }
            Uri pickImageUri = isCamera || data.getData() == null ? outputFileUri : data.getData();
            handlePickImage(context, pickImageUri);
        }
    }

    /**
     * 选择图片结果回调
     */
    private void handlePickImage(Context context, Uri imageUri) {
        if (callback != null)
            callback.onPickImage(handleUri(context, imageUri));
        outputFileUri = null;
    }

    /**
     * 兼容 Android N,Intent中不能使用 file:///*
     */
    private Uri getIntentUri(Context context, @NonNull Uri uri) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".fileProvider", new File(Objects.requireNonNull(uri.getPath())));
        } else
            return uri;
    }

    /**
     * 处理返回图片的 uri,content 协议自动转换 file 协议,避免 {@link FileNotFoundException}
     */
    private Uri handleUri(Context context, Uri uri) {
        String realPath = "";
        if (DocumentsContract.isDocumentUri(context, uri)) {//如果是document类型的Uri,则通过document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1]; // 解析出数字格式的id
                String selection = MediaStore.Images.Media._ID + "=" + id;
                realPath = getRealPathFromUri(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId));
                realPath = getRealPathFromUri(context, contentUri, null);
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {//如果是content类型的Uri,则使用普通方式处理
            realPath = getRealPathFromUri(context, uri, null);
        } else if ("file".equalsIgnoreCase(uri.getScheme()))  //如果是file类型的Uri,直接获取图片路径即可
            realPath = uri.getPath();
        if (!TextUtils.isEmpty(realPath))
            return Uri.fromFile(new File(realPath));
        else
            return uri;
    }

    /**
     * 获取文件的真实路径,比如:content://media/external/images/media/74275 的真实路径 file:///storage/sdcard0/Pictures/X.jpg
     * http://stackoverflow.com/questions/20028319/how-to-convert-content-media-external-images-media-y-to-file-storage-sdc
     */
    private String getRealPathFromUri(Context context, Uri uri, String selection) {
        Cursor cursor = null;
        try {
            String[] proj = {MediaStore.Images.Media.DATA};
            cursor = context.getContentResolver().query(uri, proj, selection, null, null);
            if (cursor == null)
                return "";
            if (cursor.moveToFirst())
                return cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA));
            return "";
        } catch (IllegalStateException e) {
            return e.getMessage();
        } finally {
            if (cursor != null)
                cursor.close();
        }
    }
}

1.2、回调类代码

PhotoPickCallback.java

java 复制代码
public interface PhotoPickCallback {

    /**
     * 用户取消回调
     */
    void onCanceled();

    /**
     * 图片返回回调
     */
    void onPickImage(@Nullable Uri imageUri);
}

第三步(使用):

调用相机

java 复制代码
                            PhotoImagePicker.getInstance().startCamera(this, new PhotoPickCallback() {
                                @Override
                                public void onCanceled() {
                                	//用户取消
                                }

                                @Override
                                public void onPickImage(@Nullable Uri imageUri) {
                                    //TODO do some things
                                }
                            });

调用相册

java 复制代码
              PhotoImagePicker.getInstance().startGallery(this, new PhotoPickCallback() {
                                @Override
                                public void onCanceled() {
                                	//用户取消
                                }

                                @Override
                                public void onPickImage(@Nullable Uri imageUri) {
                                    //TODO do some things
                                }
                            });

设置回调回传

java 复制代码
    @Override
    public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        PhotoImagePicker.getInstance().onActivityResult(this, requestCode, resultCode, data);
    }

注意事项:

  • 使用前请自行进行对应权限申请
  • 拍照图片默认保存在/storage/emulated/0/Android/data/<应用包名>/files/Pictures
相关推荐
千里马学框架3 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone4 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc4 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo6 小时前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077006 小时前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼7 小时前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone8 小时前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen8 小时前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone8 小时前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui