Android保存图片到相册,兼容Android10及以上版本

您所在的位置:网站首页 旅法师营地怎么保存图片到相册里 Android保存图片到相册,兼容Android10及以上版本

Android保存图片到相册,兼容Android10及以上版本

2024-07-10 01:46| 来源: 网络整理| 查看: 265

Android 共享存储空间

访问共享存储空间中的媒体文件

1、MediaStore概述

MediaStore是android系统提供的一个多媒体数据库,专门用于存放多媒体信息的,通过ContentResolver即可对数据库进行操作。

MediaStore.Files: 共享的文件,包括多媒体和非多媒体信息;MediaStore.Audio: 存放音频信息;MediaStore.Image: 存放图片信息;MediaStore.Vedio: 存放视频信息;

每个内部类中都又包含了Media,Thumbnails和相应的MediaColumns,分别提供了媒体信息,缩略信息和操作字段。

(1)MediaStore.Images.Media的使用——MediaStore.Images.Media.EXTERNAL_CONTENT_URI

(2)MediaStore.Video.Media的使用——MediaStore.Video.Media.EXTERNAL_CONTENT_URI

(3)MediaStore.Audio.Media的使用——MediaStore.Audio.Media.EXTERNAL_CONTENT_URI

(4)MediaStore.Downloads的使用——MediaStore.Downloads.getContentUri(“external”)

(5)MediaStore.Files的使用——MediaStore.Files.getContentUri(“external”)

2、示例

1、Android保存图片到相册,兼容Android10及以上版本:

// 保存bitmap到相册,并兼容AndroidQ public static boolean saveBitmap(Context context, Bitmap bitmap) { if (bitmap == null) { return false; } boolean isSaveSuccess; if (Build.VERSION.SDK_INT < 29) { isSaveSuccess = saveImageToGallery(context, bitmap); } else { isSaveSuccess = saveImageToGalleryQ(context, bitmap); } return isSaveSuccess; } /** * android 10 以下版本 */ private static boolean saveImageToGallery(Context context, Bitmap image) { // 首先保存图片 String storePath = AssetPathUtil.INSTANCE.getBitmapFileDir(); File appDir = new File(storePath); if (!appDir.exists()) { appDir.mkdir(); } String fileName = getCharacterAndNumber() + ".png"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); // 通过io流的方式来压缩保存图片 boolean isSuccess = image.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); // 保存图片后发送广播通知更新数据库 Uri uri = Uri.fromFile(file); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); if (isSuccess) { return true; } else { return false; } } catch (IOException e) { e.printStackTrace(); } return false; } /** * android 10 以上版本 */ private static boolean saveImageToGalleryQ(Context context, Bitmap image) { long mImageTime = System.currentTimeMillis(); String mImageFileName = getCharacterAndNumber() + ".png"; final ContentValues values = new ContentValues(); values.put(MediaStore.MediaColumns.RELATIVE_PATH, AssetPathUtil.INSTANCE.getBitmapFileDir()); values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName); values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png"); values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000); values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000); values.put(MediaStore.MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000); values.put(MediaStore.MediaColumns.IS_PENDING, 1); ContentResolver resolver = context.getContentResolver(); final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); try { OutputStream out = resolver.openOutputStream(uri); if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) { return false; } values.clear(); values.put(MediaStore.MediaColumns.IS_PENDING, 0); values.putNull(MediaStore.MediaColumns.DATE_EXPIRES); resolver.update(uri, values, null, null); } catch (Exception e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { resolver.delete(uri, null); } return false; } return true; }

2、删除相册资源(Kotlin):

/** * 保存bitmap到相册,并兼容AndroidQ */ fun getBitmapFileDir(): String { return if (Build.VERSION.SDK_INT < 29) { // android 10 以下版本 Environment.getExternalStorageDirectory().absolutePath + File.separator + BITMAP_FILE_DIRECTORY } else { Environment.DIRECTORY_PICTURES + File.separator + BITMAP_FILE_DIRECTORY } } /** * 删除保存在【相册指定目录】中的图片 */ fun deleteBitmapFileDir(context: Context) { if (Build.VERSION.SDK_INT < 29) { // android 10 以下版本 val path = getBitmapFileDir() FileUtils.deleteAllInDir(path) } else { deleteImages(context) } } /** * android 10及以上版本通过ContentResolver删除指定的目录 */ private fun deleteImages(context: Context) { var relativePath = getBitmapFileDir() //判断是否有加斜杠 if (!relativePath.endsWith("/")) { relativePath += File.separator } val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.RELATIVE_PATH) val selection = "${MediaStore.Images.Media.RELATIVE_PATH}=?" val selectionArgs = arrayOf(relativePath) context.contentResolver.delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, arrayOf(relativePath)) }

3、MediaStore 扩展方法:

/** * 和媒体相关的工具类 */ public class MediaUtils { /** * 每页获取的媒体数据量(分页加载解决相册卡顿问题) * */ private static final int PAGE_SIZE = 1000; /** * 添加到媒体数据库 * Add to media database */ public static Uri saveVideo2Album(String videoPath, int videoWidth, int videoHeight, int videoTime) { File file = new File(videoPath); if (file.exists()) { Uri uri = null; long dateTaken = System.currentTimeMillis(); ContentValues values = new ContentValues(11); // 路径; values.put(MediaStore.Video.Media.DATA, videoPath); // 标题; values.put(MediaStore.Video.Media.TITLE, file.getName()); // 时长 values.put(MediaStore.Video.Media.DURATION, videoTime * 1000); // 视频宽 values.put(MediaStore.Video.Media.WIDTH, videoWidth); // 视频高 values.put(MediaStore.Video.Media.HEIGHT, videoHeight); // 视频大小; values.put(MediaStore.Video.Media.SIZE, file.length()); // 插入时间; values.put(MediaStore.Video.Media.DATE_TAKEN, dateTaken); // 文件名; values.put(MediaStore.Video.Media.DISPLAY_NAME, file.getName()); // 修改时间; values.put(MediaStore.Video.Media.DATE_MODIFIED, dateTaken / 1000); // 添加时间; values.put(MediaStore.Video.Media.DATE_ADDED, dateTaken / 1000); values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); ContentResolver resolver = Utils.getApp().getContentResolver(); if (resolver != null) { try { uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values); } catch (Exception e) { e.printStackTrace(); uri = null; } } if (uri == null) { MediaScannerConnection.scanFile(Utils.getApp(), new String[]{videoPath}, new String[]{"video/*"}, new MediaScannerConnection.OnScanCompletedListener() { @Override public void onScanCompleted(String path, Uri uri) { } }); } return uri; } return null; } /** * Add video record by android_Q api. * AndroidQ以上,增加视频文件记录 * * @param context 上下文,the context * @param fileName 视频文件名称 the video file name * @param fileType 视频文件类型 the video file type * @param relativePath 相对路径 the relative path * @param duration 文件时长,单位是毫秒The duration of the file, in milliseconds. * @return String 类型的Uri. The String of Uri */ public static String addVideoRecord_Q(Context context, String fileName, String fileType, String relativePath, long duration) { if (!AndroidVersionUtils.isAboveAndroid_Q()) { return null; } if (TextUtils.isEmpty(relativePath)) { return null; } relativePath = "Movies/" + relativePath; ContentResolver resolver = context.getContentResolver(); //设置文件参数到ContentValues中 ContentValues values = new ContentValues(); //设置文件名 values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName); //设置文件描述,这里以文件名代替 values.put(MediaStore.Video.Media.DESCRIPTION, fileName); //设置文件类型为image/* values.put(MediaStore.Video.Media.MIME_TYPE, "video/" + fileType); values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000); values.put(MediaStore.Video.Media.DATE_MODIFIED, System.currentTimeMillis() / 1000); values.put(MediaStore.Video.Media.DURATION, duration); //注意:MediaStore.Images.Media.RELATIVE_PATH需要targetSdkVersion=29, //故该方法只可在Android10的手机上执行 values.put(MediaStore.Video.Media.RELATIVE_PATH, relativePath); //EXTERNAL_CONTENT_URI代表外部存储器 Uri external = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; //insertUri表示文件保存的uri路径 Uri insertUri = resolver.insert(external, values); return String.valueOf(insertUri); } /** * Delete video record by android_Q api * AndroidQ以上删除视频记录 * * @param context 上下文,the context * @param uri 文件的uri the uri of file * @return 删除的数量,如果是0,代表删除失败 The number of deletions. If it is 0, it means the deletion failed */ public static int deleteVideoRecord_Q(Context context, Uri uri) { context = context.getApplicationContext(); ContentResolver contentResolver = context.getContentResolver(); if (contentResolver == null) { return 0; } Cursor cursor = null; String column = MediaStore.MediaColumns._ID; int fileID = -1; try { cursor = contentResolver.query(uri, new String[]{column}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int column_index = cursor.getColumnIndexOrThrow(column); fileID = cursor.getInt(column_index); } } catch (Exception e) { LogUtils.e(e); } finally { if (cursor != null) { cursor.close(); } } if (fileID >= 0) { return contentResolver.delete(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, column + "=?", new String[]{String.valueOf(fileID)}); } return 0; } /** * 获取媒体数据列表 * Gets a list of media data * * @param type TYPE_VIDEO = 0; 视频 TYPE_PHOTO = 1;图片;YPE_ALL = 2;图片和视频 */ public static void getMediaList(final int type, final String[] filter, final MediaCallback callback, int mCurrentPage) { ThreadUtils.getCachedPool().execute(new Runnable() { @Override public void run() { final List dataList = new ArrayList(); if (type == MediaData.TYPE_ALL) { Cursor cursor = getMediaCursor(MediaData.TYPE_PHOTO, mCurrentPage); if (cursor != null) { createMediaData(cursor, filter, dataList, false); cursor.close(); } cursor = getMediaCursor(MediaData.TYPE_VIDEO, mCurrentPage); if (cursor != null) { createMediaData(cursor, filter, dataList, true); cursor.close(); } } else { Cursor cursor = getMediaCursor(type, mCurrentPage); if (cursor != null) { createMediaData(cursor,filter, dataList, type == MediaData.TYPE_VIDEO); cursor.close(); } } if (callback != null) { ThreadUtils.runOnUiThread(new Runnable() { @Override public void run() { callback.onResult(dataList); } }); } } }); } @SuppressLint("InlinedApi") private static Cursor getMediaCursor(int type, int mCurrentPage) { String[] projection = null; Uri uri = null; String order = null; if (type == MediaData.TYPE_VIDEO) { projection = new String[]{MediaStore.Video.Thumbnails._ID , MediaStore.Video.Media._ID , MediaStore.Video.Thumbnails.DATA , MediaStore.Video.Media.DURATION , MediaStore.Video.Media.SIZE , MediaStore.Video.Media.DATE_ADDED , MediaStore.Video.Media.DISPLAY_NAME , MediaStore.Video.Media.DATE_MODIFIED}; uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; order = MediaStore.Video.Media.DATE_ADDED; } else if (type == MediaData.TYPE_PHOTO) { projection = new String[]{ MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.Thumbnails.DATA, MediaStore.MediaColumns.DISPLAY_NAME }; uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; order = MediaStore.Images.Media.DATE_ADDED; } if (projection == null) { return null; } // 兼容折叠屏,在Android R及以上手机,order中禁止了LIMIT关键字,所以在这里做了适配 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { Bundle bundle = new Bundle(); bundle.putInt(ContentResolver.QUERY_ARG_OFFSET, mCurrentPage * PAGE_SIZE); bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, PAGE_SIZE); bundle.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, new String[]{order + " DESC"}); return Utils.getApp().getContentResolver().query(uri, projection, bundle, null); } else { String sortOrder = order + " DESC LIMIT " + PAGE_SIZE + " OFFSET " + mCurrentPage * PAGE_SIZE; return Utils.getApp().getContentResolver().query(uri, projection, null, null, sortOrder); } } /** * 创建媒体实体类并添加到集合中 * Create a media entity class and add it to the collection */ @SuppressLint("InlinedApi") private static void createMediaData(Cursor cursor, String[] filter, List list, boolean isVideo) { if (cursor != null) { String mediaId; String mediaDate; String mediaThumbnails; String mediaDisplayName; if (isVideo) { mediaId = MediaStore.Video.Media._ID; mediaDate = MediaStore.Video.Media.DATE_ADDED; mediaThumbnails = MediaStore.Video.Thumbnails.DATA; mediaDisplayName = MediaStore.Video.Media.DISPLAY_NAME; } else { mediaId = MediaStore.Images.Media._ID; mediaDate = MediaStore.Images.Media.DATE_ADDED; mediaThumbnails = MediaStore.Images.Thumbnails.DATA; mediaDisplayName = MediaStore.Images.Media.DISPLAY_NAME; } while (cursor.moveToNext()) { int videoId = cursor.getInt(cursor.getColumnIndex(mediaId)); String absolutePath = cursor.getString(cursor.getColumnIndex(mediaThumbnails)); String path = AndroidVersionUtils.isAboveAndroid_Q()? AndroidVersionUtils.getRealPathAndroid_Q(Uri.parse(absolutePath)): absolutePath; String displayName = cursor.getString(cursor.getColumnIndex(mediaDisplayName)); int timeIndex = cursor.getColumnIndex(mediaDate); long date = cursor.getLong(timeIndex) * 1000; if (fileIsValid(path)) { if (!TextUtils.isEmpty(absolutePath)) { int lastDot = absolutePath.lastIndexOf("."); String type = absolutePath.substring(lastDot + 1); if (!TextUtils.isEmpty(type)) { type = type.toLowerCase(); if (type.equals("mpg") || type.equals("mkv")) { continue; } //过滤文件类型 boolean isFilter = false; if (filter != null && filter.length > 0) { for (int i = 0; i < filter.length; i++) { String filterItem = filter[i]; if (StringUtils.equals(filterItem, type)) { isFilter = true; break; } } } if (isFilter) { continue; } } } MediaData mediaData = new MediaData() .setId(videoId) .setPath(path) .setDate(date) .setDisplayName(displayName); if (isVideo) { mediaData.setType(MediaData.TYPE_VIDEO) .setDuration(cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.DURATION))); } else { mediaData.setType(MediaData.TYPE_PHOTO); } if (isGif(path)){ mediaData.setIsGif(true); mediaData.setType(MediaData.TYPE_VIDEO); } list.add(mediaData); } } } } /** * 判断是否是gif图片 * Is gif boolean. * * @param path the path * @return the boolean */ public static boolean isGif(String path) { String fileName = FileUtils.getFileSuffix(path); if (!TextUtils.isEmpty(fileName) && "GIF".equals(fileName.toUpperCase())) { return true; } return false; } private static boolean fileIsValid(String filePath) { if (FileUtils.isAndroidQUriPath(filePath)) { return true; } File file = FileUtils.getFileByPath(filePath); return file != null && file.exists(); } public interface MediaCallback { void onResult(List dataList); } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3