使用 Intent.ACTION_GET_CONTENT 選取相簿圖片,在 onActivityResult 中嘗試從 data.getExtras().get("data") 取得 Bitmap,結果為 null 導致崩潰。
// 啟動選圖
Intent gallIntent = new Intent(Intent.ACTION_GET_CONTENT);
gallIntent.setType("image/*");
startActivityForResult(gallIntent, 10);
// 處理結果 - 錯誤寫法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 10 && resultCode == RESULT_OK) {
Bundle extras = data.getExtras(); // 這裡通常為 null 或無 "data" 鍵
Bitmap b = (Bitmap) extras.get("data"); // 返回 null
imgView.setImageBitmap(b); // NPE
// ...
}
}
ACTION_GET_CONTENT / ACTION_PICK 返回的是內容 URI(data.getData()),而非 Bitmapdata.getExtras().get("data") 僅在 ACTION_IMAGE_CAPTURE(相機拍照)且未指定輸出 URI 時才會包含縮圖 Bitmap
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 10 && resultCode == RESULT_OK && data != null) {
Uri imageUri = data.getData(); // 取得內容 URI
if (imageUri != null) {
try {
// 正確方式:透過 ContentResolver 解碼
Bitmap bitmap = MediaStore.Images.Media.getBitmap(
this.getContentResolver(), imageUri);
imgView.setImageBitmap(bitmap);
// 後續處理:上傳、儲存、壓縮等
// ...
} catch (IOException e) {
Log.e(TAG, "載入圖片失敗", e);
Toast.makeText(this, "圖片載入失敗", Toast.LENGTH_SHORT).show();
}
}
}
}
private Bitmap loadBitmapSafely(Uri uri, int maxWidth, int maxHeight) {
try {
// 1. 先解析尺寸不載入像素
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream is = getContentResolver().openInputStream(uri);
BitmapFactory.decodeStream(is, null, options);
is.close();
// 2. 計算縮放比例
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);
// 3. 實際載入
options.inJustDecodeBounds = false;
is = getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
is.close();
return bitmap;
} catch (IOException e) {
Log.e(TAG, "載入圖片失敗", e);
return null;
}
}
private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
int inSampleSize = 1;
if (options.outHeight > reqHeight || options.outWidth > reqWidth) {
int halfH = options.outHeight / 2;
int halfW = options.outWidth / 2;
while ((halfH / inSampleSize) >= reqHeight && (halfW / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
// Glide (Java/Kotlin)
Glide.with(this)
.load(uri)
.override(800, 800) // 限制最大尺寸
.centerCrop()
.into(imgView);
// Coil (Kotlin 專用,協程支援)
imgView.load(uri) {
crossfade(true)
transformations(CenterCropTransformation())
}
| Android 版本 | 注意事項 |
|---|---|
| Android 10 (API 29)+ | 分區儲存,直接檔案路徑無效,必須用 ContentResolver / MediaStore API |
| Android 11 (API 30)+ | 更嚴格的儲存權限,ACTION_GET_CONTENT 仍可用,ACTION_PICK 推薦用 MediaStore.Images.Media.EXTERNAL_CONTENT_URI |
| 所有版本 | Manifest 宣告 READ_EXTERNAL_STORAGE (API 33+ 改 READ_MEDIA_IMAGES) |
public class ImagePickerActivity extends AppCompatActivity {
private static final int REQUEST_GALLERY = 10;
private static final int REQUEST_CAMERA = 20;
private ImageView imgView;
private Uri cameraImageUri; // 相機拍照需預先建立輸出 URI
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_picker);
imgView = findViewById(R.id.img_view);
}
// 從相簿選取
public void pickFromGallery(View view) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, REQUEST_GALLERY);
}
// 相機拍照
public void takePhoto(View view) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 建立輸出檔案 URI (使用 FileProvider 兼容 Android 7+)
File photoFile = createImageFile();
cameraImageUri = FileProvider.getUriForFile(this,
getPackageName() + ".fileprovider", photoFile);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, REQUEST_CAMERA);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK) return;
try {
Bitmap bitmap = null;
if (requestCode == REQUEST_GALLERY && data != null) {
// 相簿:從 URI 載入
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), data.getData());
} else if (requestCode == REQUEST_CAMERA) {
// 相機:從預先指定的 URI 載入
bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), cameraImageUri);
}
if (bitmap != null) {
imgView.setImageBitmap(bitmap);
}
} catch (IOException e) {
Log.e(TAG, "圖片處理失敗", e);
}
}
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
return File.createTempFile("IMG_" + timeStamp + "_", ".jpg", storageDir);
}
}
相簿選圖返回的是 **URI**,必須用 MediaStore.Images.Media.getBitmap(ContentResolver, Uri) 或 ContentResolver.openInputStream(Uri) 載入。切勿嘗試從 data.getExtras() 取得 Bitmap。現代開發強烈建議使用 Glide/Coil 等圖片庫自動處理縮放、快取、生命週期等複雜問題。