camera1-to-camerax について
Android の旧 Camera API から CameraX への移行を支援するスキルです。Views と Compose の両方で、ライフサイクルへのバインド、プレビュー、フォーカス、撮影、カメラ切り替えを案内します。
主な機能
- 01CameraX 依存関係の設定
- 02旧カメラコードの削除
- 03ライフサイクルへのユースケースのバインド
- 04Views と Compose のプレビュー
- 05タップフォーカスと撮影
- 06前面・背面カメラの切り替え
活用シーン
- 01android.hardware.Camera を使うアプリの移行
- 02Views または Compose への CameraX プレビュー追加
- 03タップフォーカスと撮影の実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ android/ skills --skill camera1-to-cameraxインストール後、Claude Code で/camera1-to-cameraxを入力して読み込まれたか確認します。
詳細
ステップ 0:依存関係を追加する
必要な CameraX 依存関係があるか確認し、追加します。相互運用性のためにはバージョン 1.3.0 以上、Compose 拡張機能のためにはバージョン 1.5.0 以上を使用してください。
Version Catalog(libs.versions.toml)を使用している場合は、次を追加します。
[versions]
camerax = "<minimum_version_needed>"
[libraries]
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "camerax" }
androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "camerax" }
androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "camerax" }
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "camerax" }
androidx-camera-compose = { group = "androidx.camera", name = "camera-compose", version.ref = "camerax" }
また、build.gradle.kts(または build.gradle)に次を追加します。
implementation(libs.androidx.camera.core)
implementation(libs.androidx.camera.camera2)
implementation(libs.androidx.camera.lifecycle)
implementation(libs.androidx.camera.view)
implementation(libs.androidx.camera.compose)
Version Catalog を使用していない場合は、標準の Gradle 依存関係を使用します。
implementation "androidx.camera:camera-core:<minimum_version_needed>"
implementation "androidx.camera:camera-camera2:<minimum_version_needed>"
implementation "androidx.camera:camera-lifecycle:<minimum_version_needed>"
implementation "androidx.camera:camera-view:<minimum_version_needed>"
implementation "androidx.camera:camera-compose:<minimum_version_needed>"
ステップ 1:レガシー実装を削除する
android.hardware.Cameraのインスタンスをすべて削除します。SurfaceViewとSurfaceHolder.Callbackの実装(surfaceCreated、surfaceChanged、surfaceDestroyed)を削除します。onResumeまたはonPauseでカメラを開いたり解放したりする、カスタムのライフサイクル処理を削除します。- 向きに関する手動の行列計算を削除します。
ステップ 2:ProcessCameraProvider を初期化する
ProcessCameraProvider を取得し、ユースケースを Activity または Fragment のライフサイクルにバインドします。
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(context, lifecycleOwner) {
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val preview = Preview.Builder().build()
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
cameraProvider.unbindAll() // Unbind before rebinding
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview,
imageCapture
)
val cameraControl = camera.cameraControl
}, ContextCompat.getMainExecutor(context)
)
}
ステップ 3:プレビューとタップによるフォーカスを実装する
アプリの UI ツールキットに応じて、次のパターンから 1 つだけ選択します。
オプション A:Android Views(XML レガシー)の場合
androidx.camera.view.PreviewView を使用します。
1. プレビューを設定する:
preview.setSurfaceProvider(previewView.surfaceProvider)
2. タップによるフォーカスを処理する:
val factory = previewView.meteringPointFactory
val point = factory.createPoint(x, y) // x, y from touch event
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build()
cameraControl?.startFocusAndMetering(action)
オプション B:Jetpack Compose の場合
androidx.camera.compose.CameraXViewfinder を使用します。
1. プレビューと SurfaceRequest を設定する:
var surfaceRequest by remember { mutableStateOf<SurfaceRequest?>(null) }
val preview = remember {
Preview.Builder().build().apply {
setSurfaceProvider { request -> surfaceRequest = request }
}
}
2. ビューファインダーを描画する:
surfaceRequest?.let { request ->
CameraXViewfinder(
surfaceRequest = request,
coordinateTransformer = coordinateTransformer,
modifier = Modifier
)
}
3. Compose でタップによるフォーカスを処理する:
// Inside your tap gesture handler...
val surfaceCoords = with(coordinateTransformer) { offset.transform() }
val factory = SurfaceOrientedMeteringPointFactory(
request.resolution.width.toFloat(),
request.resolution.height.toFloat()
)
val point = factory.createPoint(surfaceCoords.x, surfaceCoords.y)
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build()
cameraControl?.startFocusAndMetering(action)
4. Compose のターゲット回転を更新する:
LaunchedEffect(configuration) {
if (!view.isInEditMode) {
val rotation = view.display?.rotation ?: Surface.ROTATION_0
imageCapture.targetRotation = rotation
preview.targetRotation = rotation
}
}
ステップ 4:写真を撮影する
ImageCapture ユースケースを使用して写真を撮影します。ImageProxy が回転を直接処理します。
imageCapture.takePicture(
cameraExecutor,
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
// Adjust rotation natively via ImageProxy
val matrix = Matrix()
matrix.postRotate(image.imageInfo.rotationDegrees.toFloat())
if (lensFacing == CameraSelector.LENS_FACING_FRONT) {
matrix.postScale(-1f, 1f) // Mirror for front camera
}
val rotatedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true
)
// MUST close proxy
image.close()
}
override fun onError(exception: ImageCaptureException) {
Log.e("CameraX", "Capture failed: ${exception.message}", exception)
}
}
)
ステップ 5:カメラを切り替える
フロントカメラとリアカメラを切り替えるには、CameraSelector を変更し、ProcessCameraProvider の処理を再度実行します。
lensFacing = if (lensFacing == CameraSelector.LENS_FACING_BACK) {
CameraSelector.LENS_FACING_FRONT
} else {
CameraSelector.LENS_FACING_BACK
}
制約事項
- カメラのライフサイクルを手動で管理しないでください:
ProcessCameraProviderを通じて、カメラをLifecycleOwnerにバインドします。onResumeまたはonPauseでカメラを手動で開閉する処理は避けてください。 - フォーカスの行列を手動で計算しないでください:
MeteringPointFactoryが、デバイスの回転オフセットを含む座標変換を処理します。カスタムの行列実装は避けてください。 - ImageProxy を閉じ忘れないでください:撮影コールバック内で
image.close()を呼び出してください。この呼び出しを省略すると撮影パイプラインがロックされ、その後の写真撮影が中断されます。 - Compose コードで
PreviewViewをAndroidViewにラップしないでください:Compose の UI レイアウトでは、CameraXViewfinderを使用します。PreviewViewをAndroidView内で使用するのは、サイズ変更の問題を引き起こす古い代替手段です。