Androidカメラのプレビュー表示（Camera API + TextureView）

# 前書き
前編では[Androidカメラのプレビュー表示（Camera API + SurfaceView）](https://qiita.com/Rei_2020/items/b1bb2e9fbec7d9cc0830)について述べました。本編ではそれを踏まえて、Camera API + TextureViewの実装方法を紹介します。注意点は前編と同じで、TextureViewについての注意点は特にありません。

実装方法はCamera API + SurfaceViewのとかなり被る部分があるので、ここではあえて実装の差分のみ説明します。[Androidカメラのプレビュー表示（Camera API + SurfaceView）](https://qiita.com/Rei_2020/items/b1bb2e9fbec7d9cc0830)と合わせてお読みいただければ幸いです。

# 実装
## レイアウト
SurfaceViewをTextureViewに置き換えます。

```xml:main_fragment.xml
<?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に置き換えます。

```kotlin:MyActivity.kt
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に置き換えます。

```kotlin:MyActivity.kt
// 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の双方向変換は簡単にできてしまいます。

# GitHub
* [https://github.com/Rei2020GitHub/MyPublicProject/tree/master/Camera_TextureView](https://github.com/Rei2020GitHub/MyPublicProject/tree/master/Camera_TextureView)

# 参考

* [Androidカメラのプレビュー表示（Camera API + SurfaceView）](https://qiita.com/Rei_2020/items/b1bb2e9fbec7d9cc0830)
* [Androidカメラのプレビュー表示（Camera2 API + SurfaceView）](https://qiita.com/Rei_2020/items/63fe4216835141d03c4a)
* [Androidカメラのプレビュー表示（Camera2 API + TextureView）](https://qiita.com/Rei_2020/items/05630308ae8385433505)