前言
拍照图片选择是个老生常谈的问题,每次都会花不少时间解决,虽然现在框架非常丰富,以前也都是一带而过,印象并不深刻,但是碍于维护项目不想大改,这里就做一个笔记,涉及内容还是很多的,尤其是拍照时的临时文件不能重名,这个BUG算是试出来的,没有找参考文献。
申明
fileprovider
<provider
android:name=".YourFileProvider"
android:authorities="com.proginn.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
FileProvider 自定义一个,避免冲突
package com.xiaomakj.db;
import android.support.v4.content.FileProvider;
public class YourFileProvider extends FileProvider {
}
共享说明 网上对这个说明不是很到位
- external-path 说明是SD卡路径
- path ="." 说明是整个SD卡,比如你只想共享
/sdcard/DCIM/Screenshots/Screenshot_2020-11-16-21-57-11-82_ae13a9d158c6e5c927015456b627b01c.jpg
可以写成DCIM/Screenshots/
<?xml version="1.0" encoding="utf-8"?>
<resources>
<external-path
name="external-path"
path="."/>
<root-path
name="name"
path="."/>
</resources>
这里推荐一个 腾讯IM的file_paths_public,直接拿去用吧,全共享,一步到位。
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--内置SD卡 Environment.getExternalStorageDirectory() .表示共享所有的目录,也可以指定共享的目录-->
<external-path
name="external-path"
path="."/>
<!--内置SD卡 Context.getExternalCacheDir() .表示共享所有的目录,也可以指定共享的目录-->
<external-cache-path
name="external-cache-path"
path="."/>
<!--内置SD卡 Context.getExternalFilesDir(null) .表示共享所有的目录,也可以指定共享的目录-->
<external-files-path
name="external-files-path"
path="."/>
<!--data目录下 Context.getFilesDir() .表示共享所有的目录,也可以指定共享的目录-->
<files-path
name="files_path"
path="."/>
<!--data缓存目录 Context.getCacheDir() .表示共享所有的目录,也可以指定共享的目录-->
<cache-path
name="cache-path"
path="."/>
<!--这个标签Android官方文档中是没有提及,Android设备的根目录,该目录下包含着手机内部存储器,外置SD卡等所有文件的目录-->
<root-path
name="name"
path="."/>
</paths>
定义
public class ImageProcess {
public static final int intent_request_code_camera = 1;
public static final int intent_request_code_album = 2;
public static final int intent_request_code_tallor = 3;
public static final String AVATAR_NAME = "acatar.jpg";
public static final String TEMP_PIC_NAME = "temp.jpg";
private FragmentActivity mActivity;
private ListDialogFragment listDialogFragment;
/**
* 下载下来的图片的缓存地址
*/
public static String PATH_CACHE_IMAGE = Environment.getExternalStorageDirectory() + "/Demo/cache/image/";
public static String PATH_AVATER = Environment.getExternalStorageDirectory() + "/Demo/avater/";
private File cacheImageFile;
private File avaterFile;
public File getAvaterFile() {
return makeFile(PATH_AVATER);
}
public File getCacheImageFile() {
return makeFile(PATH_CACHE_IMAGE);
}
private File makeFile(String path) {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
return file;
}
public void takePicture(FragmentActivity activity, Runnable runnable) {
PermissionUtil.requestPermission(activity, runnable, new Runnable() {
@Override
public void run() {
ToastHelper.toast("请开启相机权限");
}
}, Manifest.permission.CAMERA);
}
private int outputX = 200;
private int outputY = 200;
public ImageProcess(FragmentActivity mActivity) {
this.mActivity = mActivity;
}
public void setOutput(int outputX, int outputY) {
this.outputX = outputX;
this.outputY = outputY;
}
/**
* 拍照选择
*/
@SuppressLint("CheckResult")
private void CameraSelect() {
takePicture(mActivity, new Runnable() {
@Override
public void run() {
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 下面这句指定调用相机拍照后的照片存储的路径
createTime++;
final File file = createTempFile();
if (file == null) {
com.cjoe.utils.ToastHelper.toast("无法创建图片文件,请确保开启了 SD 卡权限");
return;
}
// Uri uri = Uri.fromFile(file);
// intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
// mActivity.startActivityForResult(intent, ImageProcess.intent_request_code_camera);
if (Build.VERSION.SDK_INT < 24) {
Uri uri = Uri.fromFile(file);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mActivity.startActivityForResult(openCameraIntent,
ImageProcess.intent_request_code_camera);
} else {
//适配Android7.0
ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, file.getAbsolutePath());
Uri uri = mActivity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
// mActivity.grantUriPermission(mActivity.getPackageName(), uri, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
openCameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
mActivity.startActivityForResult(openCameraIntent, ImageProcess.intent_request_code_camera);
}
}
});
}
static int createTime = (int) (SystemClock.elapsedRealtime()/1000);
private File createTempFile() {
final File imageFile = getTempFile();
try {
if (!imageFile.exists()) imageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
return imageFile;
}
public File getTempFile() {
return new File( getAvaterFile(), createTime + "_" + TEMP_PIC_NAME);
}
private File createAvatarFile() {
final File imageFile = getAvatarFile();
try {
if (!imageFile.exists()) imageFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
return imageFile;
}
private File getAvatarFile() {
return new File( getAvaterFile(), AVATAR_NAME);
}
/**
* 相册选择
*/
private void AlbumsSelect() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
mActivity.startActivityForResult(intent, ImageProcess.intent_request_code_album);
}
/**
* 裁剪图片方法实现
*
* @param uri
*/
private void startPhotoZoom(Uri uri) {
final File file = createAvatarFile();
if (file == null) {
ToastHelper.toast("无法创建图片文件,请确保开启了 SD 卡权限");
return;
}
Intent intent = new Intent("com.android.camera.action.CROP");
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
///storage/emulated/0/Proginn/avatertemp.jpg /storage/emulated/0/DCIM/Screenshots/Screenshot_2020-11-13-23-42-28-83.jpg content://com.android.providers.media.documents/document/image%3A763
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri newUri = FileProvider.getUriForFile(mActivity, BuildConfig.APPLICATION_ID + ".fileprovider", new File(FileUtil.getPath(mActivity, uri)));
intent.setDataAndType(newUri, "image/*");
} else {
intent.setDataAndType(uri, "image/*");
}
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", outputX);
intent.putExtra("outputY", outputY);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
// intent.putExtra(MediaStore.EXTRA_OUTPUT, getUriForFile(mActivity, file));
try {
mActivity.startActivityForResult(intent, ImageProcess.intent_request_code_tallor);
} catch (Exception e) {
Toast.makeText(mActivity, "无法打开裁切 " + e.toString(), Toast.LENGTH_SHORT).show();
Log.i("ImageProcess", "---------------------------" + e.toString());
}
}
public void activityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case ImageProcess.intent_request_code_album:
if (data != null && data.getData() != null) {
startPhotoZoom(data.getData());
}
break;
case ImageProcess.intent_request_code_camera:
if (resultCode == Activity.RESULT_OK) {
startPhotoZoom(Uri.fromFile(getTempFile()));
}
break;
default:
break;
}
}
/**
* 图片裁剪之后在onActivityResult中调用
*
* @param data
* @return
*/
public Bitmap activityResult(Intent data) {
if (data != null) {
try {
Uri uri = data.getData();
if (uri == null) {
// 高版本上上述 uri 为 null
uri = Uri.fromFile(getAvatarFile());
}
InputStream inputStream = mActivity.getContentResolver().openInputStream(uri);
return BitmapFactory.decodeStream(inputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return null;
}
public Uri getUriForFile(Context context, File file) {
if (context == null || file == null) {
throw new NullPointerException();
}
Uri uri;
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(context.getApplicationContext(),
BuildConfig.APPLICATION_ID + ".fileprovider", file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
}
使用 在Activity中使用
ImageProcess imageProcess = new ImageProcess(this);
imageProcess.setOutput(300, 186);
回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case ImageProcess.intent_request_code_album:
// setImage(imageProcess.getBitmap(data), type);
imageProcess.activityResult(requestCode, resultCode, data);
break;
case ImageProcess.intent_request_code_camera:
if (resultCode == Activity.RESULT_OK) {
imageProcess.activityResult(requestCode, resultCode, data);
// setImage(imageProcess.getBitmap(data), type);
}
break;
case ImageProcess.intent_request_code_tallor:
imageProcess.activityResult(data);
/*注意 此处已经是一张bitmap了 保存还是回显自己决定*/
default:
break;
}
}
关于文件路径得获取方法 这边建议直接搜索FileUtil 或者去github上找AndroidUtil,RXUtil等优秀得轮子。
/**
* 专为Android4.4以上设计的从Uri获取文件路径
*/
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("raw:")) {
final String path = id.replaceFirst("raw:", "");
return path;
}
String[] contentUriPrefixesToTry = new String[]{
"content://downloads/public_downloads",
"content://downloads/my_downloads",
"content://downloads/all_downloads"
};
for (String contentUriPrefix : contentUriPrefixesToTry) {
Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
try {
String path = getDataColumn(context, contentUri, null, null);
if (path != null) {
return path;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 在某些android8+的手机上,无法获取路径,所以用拷贝的方式,获取新文件名,然后把文件发出去
String fileName = getFileName(context, uri);
File cacheDir = getDocumentCacheDir(context);
File file = generateFileName(fileName, cacheDir);
String destinationPath = null;
if (file != null) {
destinationPath = file.getAbsolutePath();
saveFileFromUri(context, uri, destinationPath);
}
return destinationPath;
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}