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