1、创建图片管理工具类
/**
* @introduction 图片捕获管理工具
*/
public class ImageCaptureManager {
private final static String CAPTURED_PHOTO_PATH_KEY = "mCurrentPhotoPath";
private final static String yyyy_MM_dd_HH_mm_ss = "yyyyMMdd_HH时mm分ss";
private Context mContext;
/**
* 当前图片路径,ImageCaptureManager传入Context对象初始化后调用createImageFile后才会有。
*/
private String mCurrentPhotoPath;
/**
* 用于保存图片Uri
* 因为文件系统的关系,使用uri来访问图片
* 示例:content://com.android.providers.media.documents/document/image%3A1214406
*/
private Uri mCurrentPhotoPathUri;
public ImageCaptureManager(Context mContext) {
this.mContext = mContext;
}
/**
* 创建图片文件
*
* @return
* @throws IOException
*/
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss).format(new Date());
String imageFileName = "IMG_" + timeStamp;
File storageDir = StorageUtil.getImageDir(mContext);
File image = new File(storageDir, imageFileName + ".jpg");
mCurrentPhotoPath = image.getAbsolutePath();
mCurrentPhotoPathUri = CheckUtil.getUriForFile(mContext, image);
Log.d("ImageCaptureManager", "createImageFile: " + mCurrentPhotoPathUri.toString());
return image;
}
/**
* 吊起系统的拍照
*
* @return
* @throws IOException
*/
public Intent dispatchTakePictureIntent() throws IOException {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
//11以前
if (takePictureIntent.resolveActivity(mContext.getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = createImageFile();
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCurrentPhotoPathUri);
}
} else {
if (mContext.getPackageManager().hasSystemFeature(mContext.getPackageManager().FEATURE_CAMERA_ANY)) {
//相机可用
// Create the File where the photo should go
File photoFile = createImageFile();
// Continue only if the File was successfully created
if (photoFile != null) {
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mCurrentPhotoPathUri);
}
}
}
return takePictureIntent;
}
public String getCurrentPhotoPath() {
return mCurrentPhotoPath;
}
public Uri getCurrentPhotoPathUri() {
return mCurrentPhotoPathUri;
}
/**
* 保存状态的方法,需要在调用此类的dispatchTakePictureIntent拉起图片模块的宿主调用
*
* @param savedInstanceState
*/
public void onSaveInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null && mCurrentPhotoPath != null) {
savedInstanceState.putString(CAPTURED_PHOTO_PATH_KEY, mCurrentPhotoPath);
}
}
/**
* 回复状态的方法,需要在调用此类的dispatchTakePictureIntent拉起图片模块的宿主调用
*
* @param savedInstanceState
*/
public void onRestoreInstanceState(Bundle savedInstanceState) {
if (savedInstanceState != null && savedInstanceState.containsKey(CAPTURED_PHOTO_PATH_KEY)) {
mCurrentPhotoPath = savedInstanceState.getString(CAPTURED_PHOTO_PATH_KEY);
}
}
/**
* 简单压缩图片
*
* @param filePath
* @param targetPath 压缩后保存地址
* @return
*/
public static String compressImage(String filePath, String targetPath) {
Bitmap image = getSmallBitmap(filePath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 90;
// 循环判断如果压缩后图片是否大于1M,大于继续压缩
while (baos.toByteArray().length / 1024 > 1024) {
// 重置baos即清空baos
baos.reset();
// 这里压缩options%,把压缩后的数据存放到baos中
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
// 每次都减少10
options -= 10;
}
// 把压缩后的数据baos存放到ByteArrayInputStream中
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
// 把ByteArrayInputStream数据生成图片
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
File outputFile = new File(targetPath);
try {
if (!outputFile.exists()) {
outputFile.getParentFile().mkdirs();
} else {
outputFile.delete();
}
FileOutputStream out = new FileOutputStream(outputFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, options, out);
} catch (Exception e) {
}
return outputFile.getPath();
}
public static Bitmap getSmallBitmap(String filePath) {
final BitmapFactory.Options options = new BitmapFactory.Options();
//只解析图片边沿,获取宽高
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// 计算缩放比
options.inSampleSize = calculateInSampleSize(options, 480, 800);
// 完整解析图片返回bitmap
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
}
2、创建文件存储工具类
@SuppressWarnings("all")
public class StorageUtil {
private static final String TAG = StorageUtil.class.getSimpleName();
private StorageUtil() {
}
private enum TYPE {
FILEDIR, FILEIMG, FILECACHE, FILEAUDIO
}
/**
* 判断外存储是否挂载
*
* @return
*/
public static boolean isExternalStorageWritable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
private static File getAppDir(Context context, TYPE typeSub) {
File rootDir;
/**
* ---关于Android10的分区适配----
* 1.从 Android 4.4 到 Android 10,可以通过 Environment.getExternalStorageDirectory() 以 File Api 的方式读写。
* 2.通过Context访问自己的私有目录,不需要读写权限,不管系统是哪个版本或者是外部存储还是内部存储。
* 3.注意uri和真实路径的区别,查看res/xml/file_paths.xml中的示例。
* 4.通过Storage Access Framework的Api不需要权限,可以访问其他应用创建的文件。
* 不重要的知识:
* ① 6.0开始需要申请存储权限;
* ② Android 10开始可以做分区适配,不想做的话在配置清单application节点添加声明(requestLegacyExternalStorage = true)。
* 不过②这种方式在Android11失效了,谷歌给了开发者一个的版本的适应时间,然后逼着你适配
*/
File mFile;
if (isExternalStorageWritable()) {//有外部存储
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {//Android 11以前
//mFile👉外部存储根目录/项目名称
mFile = new File(Environment.getExternalStorageDirectory(), UsageUtil.getAppName(context));
} else {//Android11及以后,返回私有目录files
//mFile👉外部存储当前项目目录/files/项目名称
mFile = new File(context.getExternalFilesDir(null), UsageUtil.getAppName(context));
}
} else {//没有外部存储
//mFile👉内部存储当前项目目录/files
mFile = new File(context.getFilesDir(), UsageUtil.getAppName(context));
}
switch (typeSub) {
case FILEDIR:
rootDir = new File(mFile, "fs");
break;
case FILEIMG:
rootDir = new File(mFile, "images");
break;
case FILECACHE:
rootDir = new File(mFile, "caches");
break;
case FILEAUDIO:
rootDir = new File(mFile, "audios");
break;
default:
throw new IllegalStateException("Unexpected value: " + typeSub);
}
if (!rootDir.exists()) {
rootDir.mkdirs();
}
return rootDir;
}
/**
* 获取当前app文件存储目录
*
* @return
*/
public static File getFileDir(Context context) {
return getAppDir(context, TYPE.FILEDIR);
}
/**
* 获取当前app图片文件存储目录
*
* @return
*/
public static File getImageDir(Context context) {
return getAppDir(context, TYPE.FILEIMG);
}
/**
* 获取当前app缓存文件存储目录
*
* @return
*/
public static File getCacheDir(Context context) {
return getAppDir(context, TYPE.FILECACHE);
}
/**
* 获取当前app音频文件存储目录
*
* @return
*/
public static File getAudioDir(Context context) {
return getAppDir(context, TYPE.FILEAUDIO);
}
/**
* @param context
* @return 真实路径"/storage/emulated/0/Android/data/包名/cache"
*/
public static String getExternalCacheDir(Context context) {
return context.getExternalCacheDir().getAbsolutePath();
}
/**
* 创建一个文件夹, 存在则返回, 不存在则新建
*
* @param parentDirectory 父目录路径
* @param directory 目录名
* @return 文件,null代表失败
*/
public static File getDirectory(String parentDirectory, String directory) {
if (TextUtils.isEmpty(parentDirectory) || TextUtils.isEmpty(directory)) {
return null;
}
File file = new File(parentDirectory, directory);
boolean flag;
if (!file.exists()) {
flag = file.mkdir();
} else {
flag = true;
}
return flag ? file : null;
}
/**
* 根据输入流,保存文件
* 类型:直接覆盖文件
*
* @param file
* @param is
* @return
*/
public static boolean writeFile(File file, InputStream is) {
OutputStream os = null;
try {
//在每次调用的时候都会覆盖掉原来的数据
os = new FileOutputStream(file);
byte data[] = new byte[1024];
int length = -1;
while ((length = is.read(data)) != -1) {
os.write(data, 0, length);
}
os.flush();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
return false;
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
return false;
} finally {
closeStream(os);
closeStream(is);
}
}
/**
* 删除文件或文件夹
*
* @param file
*/
public static void deleteFile(File file) {
try {
if (file == null || !file.exists()) {
return;
}
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null && files.length > 0) {
for (File f : files) {
if (f.exists()) {
if (f.isDirectory()) {
deleteFile(f);
} else {
f.deleteOnExit();
Log.d(TAG, "删除文件 " + f.getAbsolutePath());
}
}
}
}
} else {
file.deleteOnExit();
Log.d(TAG, "删除文件 " + file.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 保存文件
*
* @param inputStream 输入流,比如获取网络下载的字节流 ResponseBody.byteStream()
* @param outputStream 输出流,比如FileOutputStream则是保存文件
* @return
*/
public static boolean saveFile(InputStream inputStream, OutputStream outputStream) {
if (inputStream == null || outputStream == null) {
return false;
}
try {
try {
byte[] buffer = new byte[1024 * 4];
while (true) {
int read = inputStream.read(buffer);
if (read == -1) {
break;
}
outputStream.write(buffer, 0, read);
}
outputStream.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
inputStream.close();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
/**
* 关闭流
*
* @param closeable
*/
public static void closeStream(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException e) {
throw new RuntimeException("关闭流失败!", e);
}
}
}
/**
* 通过uri拿到图片文件真实路径
*
* @param context
* @param uri
* @return
* @deprecated Android10开始,MediaStore.Images.ImageColumns.DATA被标记为过期
*/
@Deprecated
public static String getImgRealFilePath(final Context context, final Uri uri) {
if (null == uri) {
return null;
}
final String scheme = uri.getScheme();
String data = null;
if (scheme == null) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
final Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
final int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
}
3、检查工具类
public class CheckUtil {
//与AndroidManifest.xml中FileProvider的权限字段一致
public static final String AUTHS="AndroidManifest.xml中FileProvider的权限字段一致";
public static Uri getUriForFile(Context context, File file) {
if (context == null || file == null) {
throw new NullPointerException();
}
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context.getApplicationContext(), AUTHS, file);
} else {
uri = Uri.fromFile(file);
}
return uri;
}
}
4、配置信息
public interface ImagePickerConstant {
/**
* 普通回调监听
*/
interface SampleResultListener<T>{
void onSuccess(T Data);
void onFailure(T Data);
}
/**
* 普通回调监听
*/
interface NormalResultListener{
void onSuccess();
void onFailure();
}
interface GetSelectMode {
int getSelectMode();
}
int REQUEST_TAKE_PHOTO = 2;
/**
* 预览请求状态码
*/
int REQUEST_PREVIEW = 99;
String mimeTypeImage="image/*";
/** 图片选择模式,int类型 */
String EXTRA_SELECT_MODE = "select_count_mode";
/** 单选 */
int MODE_SINGLE = 0;
/** 多选 */
int MODE_MULTI = 1;
/** 最大图片选择次数,int类型 */
String EXTRA_SELECT_COUNT = "max_select_count";
/** 默认最大照片数量 */
int DEFAULT_MAX_TOTAL= 9;
/** 是否显示相机,boolean类型 */
String EXTRA_SHOW_CAMERA = "show_camera";
/** 默认选择的数据集 */
String EXTRA_DEFAULT_SELECTED_LIST = "default_result";
/** 筛选照片配置信息 */
String EXTRA_IMAGE_CONFIG = "image_filter_configuration";
/** 选择结果,返回为ArrayList<String>图片路径集合 */
String EXTRA_RESULT = "pathList_result";
/**
* 预览的图片列表
*/
String EXTRA_PHOTOS = "extra_photos";
/**
* 当前预览照片位置
*/
String EXTRA_CURRENT_ITEM = "extra_current_item";
/**
* 预览界面是否显示删除Menu
*/
String EXTRA_IS_SHOW_DELETE = "extra_is_show_delete";
}
最后在AndoridManifest.xml中添加provider_paths.xml
<provider
android:name=".utils.FileProviderUtils"
android:authorities="com.xxx.xxx.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
xml代码
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--<external-path path="xxx(目录)/" name="files_path" />-->
<root-path
name="root_path"
path="/" /> <!--内部路径相关api有关,涉及api较多-->
<files-path
name="my_files"
path="fs/" /><!--与Context.getFilesDir()有关-->
<files-path
name="my_images"
path="images/" />
<files-path
name="my_caches"
path="caches/" />
<files-path
name="my_audios"
path="audios/" />
<external-path
name="external"
path="/" /><!--与Environment.getExternalStorageDirectory()有关-->
<external-files-path
name="my_files"
path="fs/" /><!--与Context#getExternalFilesDir(String)和Context.getExternalFilesDir(null)有关-->
<external-files-path
name="my_images"
path="images/" />
<external-files-path
name="my_caches"
path="caches/" />
<external-files-path
name="my_audios"
path="audios/" />
<external-cache-path
name="external_cache_path"
path="/" /> <!--与Context.getExternalCacheDir()有关-->
</paths>
调用
/**声明
* 图片管理工具ImageCaptureManager
*/
private ImageCaptureManager imageCaptureManager;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initView();
}
private void initView() {
imageCaptureManager = new ImageCaptureManager(requireActivity());
//触发事件触发
Intent intent = null;
try {
intent = imageCaptureManager.dispatchTakePictureIntent();
startActivityForResult(intent, ImagePickerConstant.REQUEST_TAKE_PHOTO);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
/**相机拍照完成后,返回图片路径*/
case ImagePickerConstant.REQUEST_TAKE_PHOTO:
if (imageCaptureManager.getCurrentPhotoPath() != null) {
String images = imageCaptureManager.getCurrentPhotoPath();
}
break;
//通知刷新图库
default:
break;
}
}
就可以了