0Pricing
Android Academy · レッスン

写真を撮影する

画像を撮影して保存します。

「写真を撮影する」はCoddyKit上の無料Android Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAndroid Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Android Academyコースには全4レッスンが含まれています。

写真の撮影

画面にプレビューを表示できたら、次は写真を撮影します。CameRXでは、ImageCaptureユースケースを使ってこれを処理します。

ImageCaptureをプレビューと一緒にバインドし、ユーザーがシャッターボタンをタップしたときにtakePicture()を呼び出して、結果をデバイスのギャラリーに保存します。

ImageCaptureユースケースの構築

ImageCaptureはビルダーで設定します。便利なオプションに撮影モードがあります。これは、遅延と画質のバランスを調整するものです。

  • CAPTURE_MODE_MINIMIZE_LATENCY — シャッターを高速化します。
  • CAPTURE_MODE_MAXIMIZE_QUALITY — 少し遅くなりますが、最高画質で撮影します。
val imageCapture = ImageCapture.Builder()
    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
    .build()

PreviewとImageCaptureのバインド

複数のユースケースを一度にバインドできます。previewとimageCaptureの両方をbindToLifecycleに渡すと、カメラでライブ映像を表示しながら、写真を撮影できる状態にできます。

cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    preview,
    imageCapture
)

保存先:MediaStore

写真を保存する現代的でScoped Storageに対応した方法は、MediaStoreを使うことです。ContentValuesでファイル(名前、MIMEタイプ、フォルダー)を指定すると、システムが共有Picturesコレクションに配置します。

Android 10以降では、従来のWRITE_EXTERNAL_STORAGE権限なしで動作します。

val name = "photo_${System.currentTimeMillis()}"
val contentValues = ContentValues().apply {
    put(MediaStore.MediaColumns.DISPLAY_NAME, name)
    put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
    put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/MyApp")
}

出力オプション

JPEGの書き込み先をCameraXに伝えるには、コンテンツリゾルバー、対象のコレクションURI、ContentValuesからOutputFileOptionsを構築します。

val outputOptions = ImageCapture.OutputFileOptions
    .Builder(
        context.contentResolver,
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        contentValues
    )
    .build()

takePicture()の呼び出し

ここで撮影を開始します。takePicture()には、出力オプション、Executor、コールバックを渡します。CameRXは成功またはエラーを非同期で通知します。

ContextCompat.getMainExecutor(context)を使うと、コールバックがメインスレッドで実行されます。

imageCapture.takePicture(
    outputOptions,
    ContextCompat.getMainExecutor(context),
    object : ImageCapture.OnImageSavedCallback {
        override fun onImageSaved(output: ImageCapture.OutputFileResults) {
            val savedUri = output.savedUri
            // Photo saved! Show a confirmation.
        }
        override fun onError(exc: ImageCaptureException) {
            // Handle the failure.
        }
    }
)

結果の処理

成功すると、OutputFileResults.savedUriから保存した画像のURIを取得できます。これを(Coilで)表示したり、共有したり、保存が完了したことを知らせるSnackbarを表示したりできます。

エラーが発生した場合は、ImageCaptureExceptionから問題の内容を確認できます。ログに記録し、ユーザーに知らせてください。

override fun onImageSaved(output: ImageCapture.OutputFileResults) {
    val uri = output.savedUri ?: return
    Toast.makeText(context, "Saved: $uri", Toast.LENGTH_SHORT).show()
}

シャッターボタンの接続

Composeでは、Boxを使ってプレビューの上にシャッターボタンを重ねます。ImageCaptureの参照をホイスティングして、ボタンのonClickからtakePhotoを呼び出せるようにします。

Box(modifier = Modifier.fillMaxSize()) {
    CameraPreview(imageCapture = imageCapture)
    IconButton(
        onClick = { takePhoto(context, imageCapture) },
        modifier = Modifier
            .align(Alignment.BottomCenter)
            .padding(32.dp)
    ) {
        Icon(Icons.Default.PhotoCamera, contentDescription = "Take photo")
    }
}

takePhoto()の切り出し

キャプチャ処理をヘルパーに移動して、コンポーザブルをすっきり保ちます。ヘルパーはコンテキストとImageCaptureユースケースを受け取り、出力オプションを作成してtakePictureを呼び出します。

fun takePhoto(context: Context, imageCapture: ImageCapture) {
    val values = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, "IMG_${System.currentTimeMillis()}")
        put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
    }
    val options = ImageCapture.OutputFileOptions.Builder(
        context.contentResolver,
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        values
    ).build()

    imageCapture.takePicture(
        options,
        ContextCompat.getMainExecutor(context),
        object : ImageCapture.OnImageSavedCallback {
            override fun onImageSaved(o: ImageCapture.OutputFileResults) {}
            override fun onError(e: ImageCaptureException) {}
        }
    )
}

フラッシュと回転

仕上げとして、次の2点を設定します。

  • フラッシュ:imageCapture.flashModeにFLASH_MODE_ON、OFF、またはAUTOを設定します。
  • 回転:CameRXはターゲットの回転を使って、保存するJPEGを正しい向きにします。正確な向きを保つため、常に最新の値に更新してください。
imageCapture.flashMode = ImageCapture.FLASH_MODE_AUTO
// Update target rotation when the device rotates:
imageCapture.targetRotation = display.rotation

すべてを組み合わせる

写真撮影の一連の流れは次のとおりです。ImageCaptureを作成する → プレビューとバインドする → シャッターをタップしたらOutputFileOptionsを作成してtakePictureを呼び出す → onImageSaved / onErrorを処理する。

これだけの数行で、ギャラリーに保存できるカメラが完成します。

確認テスト

写真撮影の流れを理解できているか確認しましょう。

まとめ

CameRXで写真を撮影して保存できるようになりました。

  • ImageCaptureユースケースを作成し、プレビューとバインドします。
  • ContentValuesとMediaStoreを使い、スコープ付きストレージ向けの出力を指定します。
  • OutputFileOptions、エグゼキュータ、OnImageSavedCallbackを指定してtakePicture()を呼び出します。
  • 成功時はsavedUriを読み取り、エラー時はImageCaptureExceptionを処理します。

次は、VideoCaptureユースケースを使って動画を録画します。

よくある質問

「写真を撮影する」レッスンは無料ですか?

はい。「写真を撮影する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Android Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Android Academyコースには全4レッスンが含まれています。

「写真を撮影する」で何を学びますか?

画像を撮影して保存します。 ブラウザで直接実行するハンズオンコードでAndroid Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Android Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAndroid Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「写真を撮影する」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAndroid Academyレッスンでコードを書いて実行できますか?

はい。すべてのAndroid Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. CameraXの概要
  2. カメラプレビューを表示する
  3. 写真を撮影する
  4. 動画を録画する
← Android Academyに戻る