首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android 相機拍照返回空白圖片:正確使用 MediaStore ACTION_IMAGE_CAPTURE

     編輯:程式設計綜合問答

問題現象

使用 Camera API 直接拍照,回調中得到空白/全黑圖片。

原因分析

  • 預覽未就緒就拍照:startPreview() 後需等待幾幀穩定
  • 參數設定錯誤:畫面尺寸、對焦模式、曝光補償不當
  • 相機硬體問題:部分裝置需特定參數組合
  • 回調資料處理錯誤:jpegCallback 中 byte[] 處理不當

推薦方案:使用系統相機 Intent (最穩健、最簡單)


public class CameraActivity extends AppCompatActivity {
    private static final int CAMERA_REQUEST = 1888;
    private String currentPhotoPath;
    private ImageView imageView;
    
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        imageView = findViewById(R.id.image_view);
        
        dispatchTakePictureIntent();
    }
    
    private void dispatchTakePictureIntent() {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            if (photoFile != null) {
                Uri photoUri = FileProvider.getUriForFile(this,
                    "com.example.fileprovider", photoFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
                takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
            startActivityForResult(takePictureIntent, CAMERA_REQUEST);
        }
    }
    
    @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == CAMERA_REQUEST && resultCode == RESULT_OK) {
            // 若指定 EXTRA_OUTPUT,data 可能為 null,直接讀取檔案
            Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath);
            imageView.setImageBitmap(bitmap);
        }
    }
    
    private File createImageFile() throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(imageFileName, ".jpg", storageDir);
        currentPhotoPath = image.getAbsolutePath();
        return image;
    }
}

若必須用 Camera API (舊版參考)


// 關鍵修正點:
Camera camera = Camera.open();
Camera.Parameters params = camera.getParameters();

// 1. 設定合適的預覽/拍照尺寸 (從支援列表中選擇)
List sizes = params.getSupportedPictureSizes();
Camera.Size bestSize = sizes.get(0); // 簡單取第一個,實際應選最接近螢幕比例
params.setPictureSize(bestSize.width, bestSize.height);

// 2. 對焦模式設為連續對焦 (拍照最穩定)
params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

// 3. 關閉閃光燈 (避免干擾)
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);

camera.setParameters(params);

// 4. SurfaceView 預覽就緒後再拍照
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
    @Override public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
            
            // 延遲拍照,讓預覽穩定
            new Handler().postDelayed(() -> {
                camera.takePicture(null, null, jpegCallback);
            }, 500);
        } catch (IOException e) { e.printStackTrace(); }
    }
    // ... 其他回調
});

// 5. 正確處理 jpegCallback
Camera.PictureCallback jpegCallback = (data, camera) -> {
    if (data != null && data.length > 0) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(photoFile);
            fos.write(data);
            fos.close();
            // 通知 UI 更新
            runOnUiThread(() -> imageView.setImageBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)));
        } catch (IOException e) { e.printStackTrace(); }
    }
    // 重要:拍照後需重新啟動預覽 (若要連拍)
    camera.startPreview();
};

現代最佳實踐:CameraX


// 依賴
implementation "androidx.camera:camera-core:1.3.0"
implementation "androidx.camera:camera-camera2:1.3.0"
implementation "androidx.camera:camera-view:1.3.0"

// 使用
ProcessCameraProvider provider = ProcessCameraProvider.getInstance(this).get();
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(viewFinder.getSurfaceProvider());

ImageCapture imageCapture = new ImageCapture.Builder()
    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
    .build();

CameraSelector selector = CameraSelector.DEFAULT_BACK_CAMERA;
provider.bindToLifecycle(this, selector, preview, imageCapture);

// 拍照
File photoFile = createImageFile();
OutputFileOptions options = new OutputFileOptions.Builder(photoFile).build();
imageCapture.takePicture(options, ContextCompat.getMainExecutor(this), 
    new ImageCapture.OnImageSavedCallback() {
        @Override public void onImageSaved(@NonNull ImageCapture.OutputFileResults results) {
            // 成功
            Bitmap bitmap = BitmapFactory.decodeFile(photoFile.getAbsolutePath());
            imageView.setImageBitmap(bitmap);
        }
        @Override public void onError(@NonNull ImageCaptureException exception) {
            // 失敗
        }
    });

總結建議

  1. 90% 情況用系統 Intent:`MediaStore.ACTION_IMAGE_CAPTURE` + `FileProvider`,生命週期、權限、相容性全由系統處理
  2. 需自定義 UI/連拍/即時濾鏡:用 CameraX (現代) 或 Camera API (舊版維護)
  3. 空白圖片多因預覽未穩定:延遲拍照、確認對焦模式、檢查 byte[] 長度
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved