You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java:Android11及高低版本图片存读与RecyclerView适配问题

Android 11+ 图片存读适配方案(适配Scoped Storage)

一、保存图片到图库指定文件夹

Android 11及以上强制启用Scoped Storage,无法直接通过文件路径写入公共存储目录,必须使用MediaStore API完成写入操作,以下是具体实现:

核心逻辑:

  • 构建ContentValues,指定目标文件夹(如Pictures/我的绘画作品)、文件名、MIME类型等信息
  • 通过ContentResolver将图片元数据插入MediaStore,获取可写入的Uri
  • 通过输出流将绘制的Bitmap写入该Uri
  • 通知系统图库刷新,确保图片立即显示

Java代码示例:

public void saveBitmapToGallery(Context context, Bitmap bitmap, String fileName) {
    // 指定图库中的目标文件夹:Pictures/我的绘画作品
    String relativePath = Environment.DIRECTORY_PICTURES + "/我的绘画作品";
    
    ContentValues values = new ContentValues();
    values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
    values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
    values.put(MediaStore.Images.Media.RELATIVE_PATH, relativePath);
    
    // 获取ContentResolver并插入MediaStore
    ContentResolver resolver = context.getContentResolver();
    Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    
    if (uri != null) {
        try (OutputStream outputStream = resolver.openOutputStream(uri)) {
            // 将Bitmap写入输出流,这里以PNG为例,可根据需求改为JPEG
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
            // 通知图库刷新
            resolver.notifyChange(uri, null);
        } catch (IOException e) {
            e.printStackTrace();
            // 插入失败时删除无效条目
            resolver.delete(uri, null, null);
        }
    }
}

二、读取指定文件夹图片到RecyclerView

同样通过MediaStore查询目标文件夹下的图片,避免直接访问文件路径,以下是实现方式:

核心逻辑:

  • 构建查询条件,筛选出目标文件夹下的图片
  • 从查询结果中提取图片的Uri(Android 10+不建议直接使用DATA字段)
  • 将Uri列表传给RecyclerView Adapter,通过图片加载库或ContentResolver加载图片

Java代码示例:

1. 查询目标文件夹图片

public List<Uri> queryGalleryFolderImages(Context context) {
    List<Uri> imageUris = new ArrayList<>();
    String targetFolder = Environment.DIRECTORY_PICTURES + "/我的绘画作品";
    
    // 查询需要的字段
    String[] projection = {
            MediaStore.Images.Media._ID,
            MediaStore.Images.Media.RELATIVE_PATH
    };
    
    // 筛选条件:匹配目标文件夹
    String selection = MediaStore.Images.Media.RELATIVE_PATH + " = ?";
    String[] selectionArgs = {targetFolder + "/"}; // 注意末尾的斜杠
    
    // 按添加时间倒序排列
    String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";
    
    try (Cursor cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            projection,
            selection,
            selectionArgs,
            sortOrder
    )) {
        if (cursor != null && cursor.moveToFirst()) {
            int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
            do {
                long id = cursor.getLong(idColumn);
                // 构建图片的Content Uri
                Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
                imageUris.add(uri);
            } while (cursor.moveToNext());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return imageUris;
}

2. RecyclerView Adapter中加载图片

以Glide为例(需添加Glide依赖):

@Override
public void onBindViewHolder(ImageViewHolder holder, int position) {
    Uri imageUri = imageUris.get(position);
    Glide.with(holder.itemView.getContext())
            .load(imageUri)
            .into(holder.imageView);
}

三、权限适配

  • Android 13及以上:需申请READ_MEDIA_IMAGES权限(读取图片),保存图片无需额外权限
  • Android 11-12:需申请READ_EXTERNAL_STORAGE权限(读取图片),保存图片无需WRITE_EXTERNAL_STORAGE
  • Android 10及以下:需申请WRITE_EXTERNAL_STORAGE权限(保存)和READ_EXTERNAL_STORAGE权限(读取)

在Manifest中声明权限:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29"/>

内容的提问来源于stack exchange,提问作者Ravi_Makani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 08:03:56