Android CameraXでプレビューを表示する

はじめに

先日のGoogle I/Oにて発表されたCameraXを試してみました。

本稿では、プレビュー表示までを行います。

Dependencies

// projectRoot/build.gradle
allprojects {
  repositories {
    google()
    jcenter()
  }
}

// app/build.gradle
dependencies {
    def camerax_version = "1.0.0-alpha01"
    implementation "androidx.camera:camera-core:${camerax_version}"
    implementation "androidx.camera:camera-camera2:${camerax_version}"
}

Implementation

パーミッションまわりは、PermissionsDispatcher にお任せしています。
いつもお世話になっております(感謝)

@RuntimePermissions
class CameraPreviewFragment : Fragment() {

    private lateinit var finder: TextureView

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_camera_preview, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        finder = view.findViewById(R.id.preview)
        // View描画後にカメラを開始
        finder.post { startCameraWithPermissionCheck() }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        CameraX.unbindAll()
    }

    override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array<out String>,
            grantResults: IntArray
    ) {
        onRequestPermissionsResult(requestCode, grantResults)
    }

    @NeedsPermission(Manifest.permission.CAMERA)
    fun startCamera() {
        CameraX.unbindAll()
        val preview = AutoFitPreviewBuilder.build(PreviewConfig.Builder().build(), finder)
        // 作成したUseCaseをバインド
        CameraX.bindToLifecycle(this, preview)
    }

    @OnPermissionDenied(Manifest.permission.CAMERA)
    fun close() {
        findNavController().popBackStack()
    }
}

Preview

CameraXでは、各機能をUseCaseで管理しています。

提供されているUseCaseは以下です。

プレビューの表示を行いたいので、Previewを利用します。

TextureView

TextureViewをカメラ解像度のアスペクト比に合わせる必要がありますが、大体同じような処理になってしまうので、今回は公式サンプルのUtilを活用します。

AutoFitPreviewBuilder

色々とやってくれていますが、目的の処理はこちらです。
一部改変しています。

    private fun updateTransform(
        textureView: TextureView?,
        rotation: Int?,
        newBufferDimens: Size,
        newViewFinderDimens: Size
    ) {

        // ~~
        val matrix = Matrix()
        val centerX = viewFinderDimens.width / 2f
        val centerY = viewFinderDimens.height / 2f
        matrix.postRotate(-viewFinderRotation!!.toFloat(), centerX, centerY)

        val bufferRatio = bufferDimens.width / bufferDimens.height.toFloat()

        if (viewFinderDimens.width > viewFinderDimens.height) {
            val scaledHeight = Math.round(viewFinderDimens.width * bufferRatio)
            val yScale = scaledHeight / viewFinderDimens.height.toFloat()
            matrix.preScale(1f, yScale, centerX, centerY)
        } else {
            val scaledWidth = Math.round(viewFinderDimens.height / bufferRatio)
            val xScale = scaledWidth / viewFinderDimens.width.toFloat()
            matrix.preScale(xScale, 1f, centerX, centerY)
        }

        tv.setTransform(matrix)
    }

Viewに合わせて、回転・拡縮を行います。

※回転は今までセンサー・画面向きを考慮していましたが、ライブラリ内部のCameraOrientationUtilにて対応してくれています。
単純に回転している分、マイナスで戻すだけになっています。

おわりに

TextureViewの設定(Camera2でもやっていたこと)以外、特筆すべき点もなく簡単にプレビュー表示できました!
Camera2でプレビューを表示するまでを考えると、とても簡単になったと感じます。

今までのカメラ機能は、「お作法」的な部分に時間を取られていましたが
今後は要件・仕様に合わせてカスタマイズすることに集中できそうです。
AutoFitPreviewBuilderはリファクタリングしたいと思います。

URL

Android Developer

Others