Android Studio(Java)加载手机存储图片至ImageView不显示问题求解
故障原因汇总
你遇到的点击能拿到路径但图片不显示的问题,是以下几个常见问题叠加导致的:
- 存储权限未正确配置或未动态申请
- 高版本Android系统上使用文件绝对路径加载图片,受分区存储限制无访问权限
- 媒体库查询后未关闭Cursor导致资源泄漏
- Glide加载未做容错配置,加载失败无明确提示
- 部分情况下ImageView控件宽高设置为
wrap_content,初始尺寸为0导致Glide不渲染
分步修复方案
1. 配置对应存储权限
在AndroidManifest.xml中添加对应版本的权限声明,Android 10及以上未适配分区存储时可临时开启兼容标志:
<!-- Android 12及以下版本读存储权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上版本读图片权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <application ... android:requestLegacyExternalStorage="true" ...> <!-- 你的组件声明 --> </application>
注意:Android 6.0及以上版本必须在页面中动态申请上述权限,用户授权通过后再执行媒体库查询逻辑,禁止未授权直接读取相册。
2. 替换媒体库查询逻辑,不再使用文件绝对路径
MediaStore.Images.Media.DATA字段返回的绝对路径在Android 10以上分区存储机制下,应用无直接访问权限,你点击时能拿到字符串仅代表路径值存在,不代表应用可以读取对应文件。
直接查询图片ID拼接Content Uri交给Glide加载即可,Glide原生支持通过Content Uri读取媒体资源,不需要额外文件权限,同时记得关闭Cursor避免泄漏:
public class ImageGallery { public static ArrayList<Uri> getImageList(Context context){ Uri queryUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; // 只查ID和相册名字段,不需要DATA字段 String[] projection = {MediaStore.Images.Media._ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; String orderBy = MediaStore.Images.Media.DATE_MODIFIED + " DESC"; Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, orderBy); ArrayList<Uri> imageList = new ArrayList<>(); if (cursor == null) return imageList; int idColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID); while (cursor.moveToNext()){ long imageId = cursor.getLong(idColumnIndex); // 拼接图片对应的Content Uri Uri imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, imageId); imageList.add(imageUri); } // 必须关闭Cursor释放资源 cursor.close(); return imageList; } }
3. 修改Adapter适配Uri加载
把Adapter中图片列表的类型从List<String>改为List<Uri>,优化Glide加载配置,添加占位图和缩放规则:
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.MyViewHolder>{ private Context mContext; private List<Uri> images; private PhotoListener photoListener; public GalleryAdapter(Context mContext, List<Uri> images, PhotoListener photoListener) { this.mContext = mContext; this.images = images; this.photoListener = photoListener; } @NonNull @Override public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new MyViewHolder( LayoutInflater.from(mContext).inflate(R.layout.gallery_item , parent , false) ); } @Override public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { Uri imageUri = images.get(position); Glide.with(mContext) .load(imageUri) .centerCrop() // 按控件尺寸裁剪图片,避免变形 .placeholder(R.drawable.ic_loading) // 替换为你自己的加载中占位图 .error(R.drawable.ic_load_error) // 替换为你自己的加载失败占位图 .into(holder.imageView); holder.itemView.setOnClickListener(v -> photoListener.onPhotoClick(imageUri.toString())); } @Override public int getItemCount() { return images.size(); } public class MyViewHolder extends RecyclerView.ViewHolder{ ImageView imageView; public MyViewHolder(@NonNull View itemView) { super(itemView); imageView = itemView.findViewById(R.id.ImageView); } } public interface PhotoListener{ void onPhotoClick(String path); } }
4. 检查列表项布局
确认R.layout.gallery_item布局中的ImageView宽高不要设置为wrap_content,建议设置固定尺寸或者配合约束布局设为match_constraint,避免控件初始宽高为0导致Glide无法正确渲染图片。
相册实现参考
基础相册功能核心逻辑就是上述的媒体库查询+列表加载,后续如果要实现图片预览、多选、相册分类切换,直接使用返回的Content Uri即可,不需要额外转成文件路径,系统所有图片相关接口(裁剪、分享、预览)都原生支持Content Uri传参。
内容的提问来源于stack exchange,提问作者Farhan Ali

