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

2024-06-04 3057阅读

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。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]