Android Studio中SD卡图片无法从图库选择后显示的解决方案求助
嘿,我来帮你搞定这个问题!你遇到的情况大概率是因为Android的存储权限政策变化,尤其是Android 10(API 29)及以上的分区存储机制,导致直接访问SD卡文件路径受限,加上可能权限没处理到位,所以SD卡图片加载失败。下面分步骤给你解决方案:
1. 先搞定权限问题
针对Android 10及以下(API ≤29)
你需要在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
然后在代码中动态申请权限,因为从Android 6.0(API 23)开始,危险权限需要动态申请:
private static final int REQUEST_PERMISSION = 100; // 在onCreate或者点击选择图片前检查权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION); } else { // 权限已授予,打开图库选择图片 openGallery(); } // 处理权限申请结果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == REQUEST_PERMISSION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { openGallery(); } else { Toast.makeText(this, "需要存储权限才能选择图片", Toast.LENGTH_SHORT).show(); } } }
针对Android 11及以上(API ≥30)
Android 11引入了分区存储,默认禁止直接访问外部存储的文件路径。如果是选择媒体文件(图片、视频、音频),你不需要申请READ_EXTERNAL_STORAGE权限,系统会自动授予临时访问权限;但如果是访问SD卡上的非媒体文件,你需要使用**存储访问框架(SAF)**来打开文件选择器。
2. 正确处理图片选择后的Uri加载
你原来的代码可能尝试将Uri转成File路径来加载,但SD卡的Uri(比如content://com.android.externalstorage.documents/...)无法直接转成可访问的File路径,尤其是Android 10+。正确的做法是通过ContentResolver直接读取Uri对应的数据流,或者用图片加载库(比如Glide、Picasso)来处理。
方式一:用ContentResolver加载Bitmap
修改onActivityResult(如果用的是旧的API):
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && data != null) { Uri imageUri = data.getData(); try { // 通过ContentResolver获取输入流 InputStream inputStream = getContentResolver().openInputStream(imageUri); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); img.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); Toast.makeText(this, "加载图片失败", Toast.LENGTH_SHORT).show(); } } }
方式二:用Glide(推荐,更高效且处理各种Uri)
首先在build.gradle中添加Glide依赖:
implementation 'com.github.bumptech.glide:glide:4.15.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1'
然后在onActivityResult中直接加载:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE && resultCode == RESULT_OK && data != null) { Uri imageUri = data.getData(); Glide.with(this) .load(imageUri) .into(img); } }
方式三:用新的ActivityResultContracts(替代过时的startActivityForResult)
从AndroidX Activity 1.2.0开始,推荐用ActivityResultContracts来处理跳转结果,更简洁:
// 在Activity中声明 private ActivityResultLauncher<String> pickImageLauncher; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); img = findViewById(R.id.img); btn = findViewById(R.id.btn); // 初始化图片选择器 pickImageLauncher = registerForActivityResult(new ActivityResultContracts.GetContent(), uri -> { if (uri != null) { // 用Glide加载或者ContentResolver加载 Glide.with(this).load(uri).into(img); } }); btn.setOnClickListener(v -> { // 检查权限(Android 10及以下) if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERMISSION); return; } } // 打开图片选择器,指定类型为图片 pickImageLauncher.launch("image/*"); }); }
3. 关键注意点
- 不要尝试将SD卡的
content://Uri转成File路径,这在Android 10+几乎是行不通的,直接通过Uri加载即可。 - 如果你需要处理非媒体文件(比如文档),必须使用SAF的
OpenDocument契约,而不是GetContent。 - 确保你的
ImageView有足够的大小,避免图片加载后显示不出来(比如设置wrap_content或者固定尺寸)。
按上面的步骤修改后,应该就能正常加载SD卡中的图片啦!
内容的提问来源于stack exchange,提问作者AyoubS

