Android保存图片到相册,兼容Android10及以上版本
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 = 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
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
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理!
部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!
图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!