0Pricing
Android Academy · レッスン

カメラプレビューを表示する

ライブプレビューを接続します。

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

目標:ライブプレビュー

ほとんどすべてのカメラアプリにまず必要なのは、ライブプレビューです。これはカメラが捉えている映像をリアルタイムで表示するものです。このレッスンでは、カメラ映像を全画面に表示するCompose画面を作成します。

PreviewView、Previewユースケース、バインドされたProcessCameraProviderの3つを組み合わせます。

カメラ権限のリクエスト

プレビューを表示する前に、ユーザーの許可が必要です。Composeでは、Accompanist(または公式の権限API)を使うと、rememberPermissionStateで簡単に処理できます。

権限がgrantedになってからカメラを開始してください。

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
    val permission = rememberPermissionState(Manifest.permission.CAMERA)

    LaunchedEffect(Unit) { permission.launchPermissionRequest() }

    if (permission.status.isGranted) {
        CameraPreview()
    } else {
        Text("Camera permission is required.")
    }
}

ComposeへのPreviewViewの組み込み

PreviewViewは従来のAndroid Viewなので、AndroidViewを使ってCompose内に配置します。factoryラムダでビューを一度だけ作成し、設定に使える参照を取得します。

作成したビューを保持し、カメラのサーフェスを接続できるようにします。

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { context ->
            PreviewView(context).apply {
                scaleType = PreviewView.ScaleType.FILL_CENTER
            }
        }
    )
}

LifecycleOwnerの取得

カメラをバインドするにはLifecycleOwnerが必要です。ComposeではLocalLifecycleOwner.currentで現在のものを読み取り、LocalContext.currentでコンテキストを取得します。

カメラプロバイダーを設定してユースケースをバインドするには、両方が必要です。

val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

Previewユースケースの構築

Previewユースケースは、フレームをサーフェスへストリーミングします。構築した後、ビューのサーフェスプロバイダーをsetSurfaceProviderに渡して、PreviewViewへ接続します。

これにより、カメラ映像が実際に画面へ表示されます。

val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewView.surfaceProvider)

コルーチン内でのバインド

プロバイダーの取得は非同期なので、コルーチン内で行います。ビューに関連付けたLaunchedEffectを使うと、処理を一度だけ実行できます。その中でプロバイダーを待ち、以前のバインドを解除してから、プレビューをバインドします。

LaunchedEffect(previewView) {
    val cameraProvider = ProcessCameraProvider.getInstance(context).await()
    val preview = Preview.Builder().build().also {
        it.setSurfaceProvider(previewView.surfaceProvider)
    }
    cameraProvider.unbindAll()
    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        CameraSelector.DEFAULT_BACK_CAMERA,
        preview
    )
}

await()拡張関数

ProcessCameraProvider.getInstance()はListenableFutureを返します。kotlinx-coroutines-guavaのawait()拡張関数は、解決されるまで一時停止するため、コールバックコードを読みやすい逐次コードに変換できます。

使用するには、依存関係org.jetbrains.kotlinx:kotlinx-coroutines-guavaを追加してください。

import kotlinx.coroutines.guava.await

// Without await you'd use a listener:
// future.addListener({ val provider = future.get() }, executor)
// await() makes it a single suspend call instead.

全体を組み立てる

これがプレビュー用コンポーザブルの完全なコードです。factoryでPreviewViewを作成して取得し、その後LaunchedEffect内でバインドする流れに注目してください。

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val previewView = remember { PreviewView(context) }

    LaunchedEffect(previewView) {
        val provider = ProcessCameraProvider.getInstance(context).await()
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(previewView.surfaceProvider)
        }
        provider.unbindAll()
        provider.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview
        )
    }

    AndroidView(
        factory = { previewView },
        modifier = modifier.fillMaxSize()
    )
}

スケールタイプ

カメラセンサーのアスペクト比が画面と一致することはほとんどありません。PreviewView.ScaleTypeで、映像をビューにどのように収めるかを制御できます。

  • FILL_CENTER — ビュー全体を埋め、端を切り取ります(最も一般的)。
  • FIT_CENTER — レターボックスを付けて、フレーム全体を表示します。

全画面で没入感のあるカメラ表示にはFILL_CENTERを選択してください。

previewView.scaleType = PreviewView.ScaleType.FILL_CENTER
// or
previewView.scaleType = PreviewView.ScaleType.FIT_CENTER

カメラの切り替え

前面カメラと背面カメラを切り替えるには、セレクターを状態として保持し、変更時に再バインドします。LaunchedEffectはキーが変わると再実行されるため、セレクターをキーに含めるだけで済みます。

var selector by remember { mutableStateOf(CameraSelector.DEFAULT_BACK_CAMERA) }

LaunchedEffect(selector) {
    val provider = ProcessCameraProvider.getInstance(context).await()
    provider.unbindAll()
    provider.bindToLifecycle(lifecycleOwner, selector, preview)
}

// Flip button toggles 'selector' between front and back.

よくある落とし穴

プレビューが黒くなる原因は、主に次の2つです。

  • setSurfaceProviderを呼び忘れている — フレームの出力先がありません。
  • 再バインド前にunbindAll()を呼んでいない — ユースケースを2回バインドするとCameraXが例外をスローします。

また、権限が実際に許可されるまでプレビューは開始されないことにも注意してください。

クイックチェック

プレビューの接続方法を理解できているか確認しましょう。

まとめ

Composeでライブカメラプレビューを作成しました。

  • 開始前にCAMERA権限をリクエストします。
  • AndroidViewでPreviewViewを配置します。
  • コルーチン内でProcessCameraProviderを待ちます。
  • Previewを構築し、setSurfaceProvider、unbindAll()の順に呼び出してから、bindToLifecycleします。

次はImageCaptureユースケースを追加し、実際に写真を撮影します。

よくある質問

「カメラプレビューを表示する」レッスンは無料ですか?

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

「カメラプレビューを表示する」で何を学びますか?

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

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

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

「カメラプレビューを表示する」レッスンにはどのくらい時間がかかりますか?

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

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

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

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

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