前書き
前編ではAndroidカメラのプレビュー表示(Camera API + SurfaceView)について述べました。本編ではそれを踏まえて、Camera API + TextureViewの実装方法を紹介します。注意点は前編と同じで、TextureViewについての注意点は特にありません。
実装方法はCamera API + SurfaceViewのとかなり被る部分があるので、ここではあえて実装の差分のみ説明します。Androidカメラのプレビュー表示(Camera API + SurfaceView)と合わせてお読みいただければ幸いです。
実装
レイアウト
SurfaceViewをTextureViewに置き換えます。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/main_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:keepScreenOn="true">
        <!-- SurfaceViewをTextureViewに置き換える -->
        <TextureView
            android:id="@+id/texture_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
カメラ起動
camera.setPreviewDisplay(surfaceHolder)をcamera.setPreviewTexture(surfaceTexture)に置き換えます。
surfaceViewをtextureViewに置き換えます。
private fun openCamera() {
	val context = context ?: return
	val windowManager = activity?.windowManager ?: return
	// カメラIDを特定
	val cameraId = getCameraId(cameraType) ?: return
	// カメラを取得
	camera = Camera.open(cameraId)
	camera?.let { camera ->
		try {
			// camera.setPreviewDisplay(surfaceHolder)
			// カメラ出力先を指定
			// TextureViewに出力するので、setPreviewTexture()を使って、SurfaceTextureを設定
			camera.setPreviewTexture(surfaceTexture)
		} catch (ioException: IOException) {
			ioException.printStackTrace()
		}
		// [注意点]:端末の回転角度に合わせて、カメラの回転角度を設定
		camera.setDisplayOrientation(
				when (windowManager.defaultDisplay.rotation) {
					Surface.ROTATION_0 -> 90
					Surface.ROTATION_90 -> 0
					Surface.ROTATION_180 -> 270
					Surface.ROTATION_270 -> 180
					else -> 0
				}
		)
		// カメラのパラメータを設定
		cameraParam = camera.parameters.apply {
			// プレビュー可能サイズを取得
			val size = supportedPreviewSizes.firstOrNull()
			size?.let { size ->
				// プレビューサイズを設定
				setPreviewSize(size.width, size.height)
			}
		}
		// カメラのパラメータを設定
		camera.parameters = camera.parameters.apply {
			val size = supportedPreviewSizes.firstOrNull()
			size?.let { size ->
				setPreviewSize(size.width, size.height)
			}
		}
		// カメラプレビューを開始
		camera.startPreview()
		// [注意点]:プレビューサイズに合わせて、TextureViewのサイズを調整する。
		// この処理が抜けると、プレビューのアスペクト比がおかしくなる可能性がある
		// surfaceViewをtextureViewに置き換える
		updateSurfaceSize(
			// binding.surfaceView,
			binding.textureView,
			camera.parameters.previewSize.width,
			camera.parameters.previewSize.height.
   			surfaceWidth,
			surfaceHeight
		)
	}
}
TextureView
SurfaceViewをTextureViewに置き換えます。
// SurfaceHolderをSurfaceTextureに置き換える
// private var surfaceHolder: SurfaceHolder? = null
private var surfaceTexture: SurfaceTexture? = null
override fun onCreateView(
		inflater: LayoutInflater,
		container: ViewGroup?,
		savedInstanceState: Bundle?
): View? {
	binding = DataBindingUtil.inflate<MainFragmentBinding>(
			inflater,
			R.layout.main_fragment,
			container,
			false
	)
	// SurfaceViewをTextureViewに置き換える
	binding.textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {
		override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
			surfaceTexture = surface
			surfaceWidth = width
			surfaceHeight = height
			checkAndAskPermission()
		}
		override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
			surfaceWidth = width
			surfaceHeight = height
		}
		override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
		}
		override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
			return false
		}
	}
	return binding.root
}
まとめ
ご覧になればおわかりだと思いますが、Camera API + SurfaceViewとCamera API + TextureViewの差分がかなり少ないです。しかもカメラ制御方法もほぼ一緒です。コツさえつかめば、SurfaceViewとTextureViewの双方向変換は簡単にできてしまいます。

