【Androidアプリ開発】カメラ映像をUIに表示する
はじめに
AndroidのカメラをUIに表示するソースコードを解説します。UIは、
Composable
を使って構成します。
ソースコード
AndroidManifest.xml
AndroidManifest.xml
に以下を追加します。
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />
build.gradle.kts (Module :app)
モジュールレベルの build.gradle.kts
の
dependencies
に以下を追加して、依存ライブラリをインストールします。
implementation("androidx.camera:camera-camera2:1.3.1")
implementation("androidx.camera:camera-lifecycle:1.3.1")
implementation("androidx.camera:camera-view:1.3.1")
MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RtmpTestTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
// CameraPreview() に hasCameraPermission を渡します
CameraPreviewWithPermissionRequest()
}
}
}
}
}
@Composable
fun CameraPreviewWithPermissionRequest() {
var hasCameraPermission by remember { mutableStateOf(false) }
val context = LocalContext.current
// パーミッションのリクエストの結果を受け取る Launcher
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = { isGranted: Boolean ->
hasCameraPermission = isGranted
}
)
// アプリがフォアグラウンドに来たときにパーミッションの状態をチェック
LaunchedEffect(key1 = true) {
if (context.checkSelfPermission(android.Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
permissionLauncher.launch(android.Manifest.permission.CAMERA)
} else {
hasCameraPermission = true
}
}
if (hasCameraPermission) {
CameraPreview()
} else {
Text("カメラのアクセス許可が必要です。")
}
}
CameraPreview.kt
@Composable
fun CameraPreview() {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val previewView = remember { PreviewView(context) } // CameraXライブラリの一部で、カメラのプレビューを表示するためのビュー
val modifier = Modifier.fillMaxSize()
AndroidView(
modifier = modifier,
factory = { previewView },
update = { view ->
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(view.surfaceProvider)
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, preview
)
} catch(exc: Exception) {
Log.e("CameraPreview", "Use case binding failed", exc)
}
}
)
}
解説
このコードは、AndroidのJetpack
Composeを使用して、カメラのプレビューを表示するためのComposable関数(CameraPreview
)を定義しています。Composable関数は、UIを宣言的に記述するためのもので、Jetpack
Composeで使用されます。この特定の例では、カメラのプレビューを画面に表示するために、CameraXライブラリと一緒に使用されています。
ここで重要なクラスと関数について解説します:
PreviewView
PreviewView
は、CameraXライブラリの一部で、カメラのプレビューを表示するためのビューです。Androidの従来のビューシステムに属しており、Compose内で使用するためには、AndroidView
というComposable関数を介して統合する必要があります。
AndroidView
AndroidView
は、既存のAndroidビュー(この場合はPreviewView
)をComposeに統合するために使用されるComposable関数です。これにより、Composeが中心のアプリケーションでも、まだComposeに移行されていない旧式のビューコンポーネントを使用できるようになります。
コード解説
ローカルコンテキストとライフサイクルオーナーの取得
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
Compose内で、現在のコンテキストとライフサイクルオーナーを取得しています。これらはカメラのセットアップとバインディングに必要です。
CameraProviderの取得とPreviewViewのインスタンス化
val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) }
val previewView = remember { PreviewView(context) }
remember
を使用して、ProcessCameraProvider
のインスタンス(将来的な結果を含む)とPreviewView
のインスタンスを保持します。これにより、これらのオブジェクトは、Composableが再構築されるたびに再インスタンス化されることはありません。
AndroidViewを使ってPreviewViewをComposeに統合
AndroidView(
modifier = modifier,
factory = { previewView },
update = { view ->
// ...
}
)
AndroidView
Composableは、PreviewView
をComposeのUIツリーに統合します。factory
パラメータにはPreviewView
のインスタンスを渡し、update
ブロックでは、カメラのセットアップとバインディングのロジックを記述します。
カメラのプレビューをセットアップ
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(view.surfaceProvider)
}
cameraProviderFuture.get()
でCameraProviderを取得し、Preview
のインスタンスを作成して、surfaceProvider
を設定します。
カメラセレクターを設定し、ライフサイクルにバインド
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, preview
)
} catch(exc: Exception) {
Log.e("CameraPreview", "Use case binding failed", exc)
}
デフォルトのバックカメラを使用するようにカメラセレクターを設定し、cameraProvider
を使って、カメラのプレビューをライフサイクルオーナーにバインドします。これにより、アプリのライフサイクルと同期してカメラのリソースが管理されます。例外が発生した場合は、エラーログを出力します。
全体として、このコードはComposeを使用してカメラのプレビューを表示する一例ですが、実際のアプリではパーミッションの確認など、さらに多くの処理を行う必要があります。また、remember
が使用されているので、これらのインスタンスはComposableが再構築されても保持されます。