如何将 CameraView 与 Jetpack Compose 一起使用?
Posted
技术标签:
【中文标题】如何将 CameraView 与 Jetpack Compose 一起使用?【英文标题】:How can I use a CameraView with Jetpack Compose? 【发布时间】:2020-08-30 21:04:59 【问题描述】:目前 Compose 中没有相当于 CameraView(和 PreviewView)的功能。是否可以将其包装并显示在撰写布局中?
【问题讨论】:
我自己没试过,但是有webView的样例:android.googlesource.com/platform/frameworks/support/+/…android.googlesource.com/platform/frameworks/support/+/… 你能具体说明一下吗? @pentexnyx 我添加了一个包含更多细节的答案。 @HabibKazemi 伟大的一个(赞成)-但我正在和 icefex 交谈(不过我没有提到他,我的错) 【参考方案1】:仍然没有可组合的 CameraX。您需要使用AndroidView
创建一个。
更新了 Compose 1.0.0-beta02 的示例:
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
)
val lifecycleOwner = LocalLifecycleOwner.current
AndroidView(
modifier = modifier,
factory = context ->
val previewView = PreviewView(context).apply
this.scaleType = scaleType
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
// Preview is incorrectly scaled in Compose on some devices without this
implementationMode = PreviewView.ImplementationMode.COMPATIBLE
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
cameraProviderFuture.addListener(
val cameraProvider = cameraProviderFuture.get()
// Preview
val preview = Preview.Builder()
.build()
.also
it.setSurfaceProvider(previewView.surfaceProvider)
try
// Must unbind the use-cases before rebinding them.
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
lifecycleOwner, cameraSelector, preview
)
catch (exc: Exception)
Log.e(TAG, "Use case binding failed", exc)
, ContextCompat.getMainExecutor(context))
previewView
)
【讨论】:
这符合我希望找到的内容。一个问题:ProcessCameraProvider
似乎未定义。我根据参考文档here 包含了androidx.camera:camera-lifecycle:1.1.0-alpha3
。您为 ProcessCameraProvider 使用什么库和版本?谢谢!
我的依赖是:实现“androidx.camera:camera-camera2:1.0.0-rc04”实现“androidx.camera:camera-lifecycle:1.0.0-rc04”实现“androidx.camera: camera-view:1.0.0-alpha23" 以及许多其他 androidx 依赖项,但我认为那些应该这样做。我想 1.1.0 也可以,但我还没有尝试过。
谢谢。我刚刚得到它与来自 Maven 的1.1.0-alpha03
一起工作,并且正在返回报告的路上。什么时候活着! :) mvnrepository.com/artifact/androidx.camera/camera-lifecycle【参考方案2】:
目前没有任何官方 CameraX 的可组合函数,因此我们必须在 compose 中扩展旧版 android 视图。
为实现这一目标
我们可以使用AndroidView
可组合函数,
它接受两个参数
resId
要膨胀的布局资源的id。
@param postInflationCallback
布局后要调用的回调
膨胀了。
为了访问生命周期和上下文,我们使用环境
val lifecycleOwner = LifecycleOwnerAmbient.current
val context = ContextAmbient.current
既然我们有我们需要的一切,那就去做吧:
创建布局camera_host.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.camera.view.PreviewView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/previewView"
android:layout_
android:layout_ />
并使用AndroidView
Composable 函数对其进行膨胀。
@Composable
fun SimpleCameraPreview()
val lifecycleOwner = LifecycleOwnerAmbient.current
val context = ContextAmbient.current
val cameraProviderFuture = remember ProcessCameraProvider.getInstance(context)
AndroidView(resId = R.layout.camera_host) inflatedLayout ->
//You can call
// findViewById<>() and etc ... on inflatedLayout
// here PreviewView is the root of my layout so I just cast it to
// the PreviewView and no findViewById is required
cameraProviderFuture.addListener(Runnable
val cameraProvider = cameraProviderFuture.get()
bindPreview(
lifecycleOwner,
inflatedLayout as PreviewView /*the inflated layout*/,
cameraProvider)
, ContextCompat.getMainExecutor(context))
fun bindPreview(
lifecycleOwner: LifecycleOwner,
previewView: PreviewView,
cameraProvider: ProcessCameraProvider
)
var preview: Preview = Preview.Builder().build()
var cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
preview.setSurfaceProvider(previewView.createSurfaceProvider())
var camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
class MainActivity : AppCompatActivity()
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContent
SimpleCameraPreview()
【讨论】:
【参考方案3】:我创建了一个库以在 Jetpack Compose 中使用 CameraX。在官方图书馆出现之前,它可能会有用。
https://github.com/skgmn/CameraXX
在您的 build.gradle 中,(需要 GitHub 个人访问令牌)
implementation "com.github.skgmn:cameraxx-composable:0.3.0"
可组合的方法签名
CameraPreview(
modifier: Modifier = Modifier,
cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
preview: Preview?,
imageCapture: ImageCapture? = null,
imageAnalysis: ImageAnalysis? = null
)
您可以省略preview
参数以使用默认的Preview
实例。
一个例子
class MainViewModel : ViewModel()
val imageCapture = ImageCapture.Builder().build()
@Composable
fun Main()
val viewModel: MainViewModel = viewModel()
val imageCapture by remember viewModel.imageCapture
CameraPreview(Modifier.fillMaxSize(), imageCapture)
【讨论】:
【参考方案4】:这是我的 sn-p(基于 Sean 的回答),它还处理手电筒状态和资源配置,并增加了对点击逻辑的关注。 依赖:
implementation 'androidx.camera:camera-camera2:1.1.0-alpha11'
implementation 'androidx.camera:camera-view:1.0.0-alpha31'
implementation 'androidx.camera:camera-lifecycle:1.1.0-alpha11'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0-RC'
@Composable
fun CameraPreview(
modifier: Modifier = Modifier,
cameraSelector: CameraSelector = CameraSelector.DEFAULT_BACK_CAMERA,
implementationMode: PreviewView.ImplementationMode = PreviewView.ImplementationMode.COMPATIBLE,
scaleType: PreviewView.ScaleType = PreviewView.ScaleType.FILL_CENTER,
imageAnalysis: ImageAnalysis? = null,
imageCapture: ImageCapture? = null,
preview: Preview = remember Preview.Builder().build() ,
enableTorch: Boolean = false,
focusOnTap: Boolean = false
)
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
val cameraProvider by produceState<ProcessCameraProvider?>(initialValue = null)
value = ProcessCameraProvider.getInstance(context).await()
// TODO: add cameraSelector
val camera = remember(cameraProvider)
cameraProvider?.let
it.unbindAll()
it.bindToLifecycle(
lifecycleOwner,
cameraSelector,
*listOfNotNull(imageAnalysis, imageCapture, preview).toTypedArray()
)
LaunchedEffect(camera, enableTorch)
camera?.let
if (it.cameraInfo.hasFlashUnit())
it.cameraControl.enableTorch(enableTorch).await()
DisposableEffect(Unit)
onDispose
cameraProvider?.unbindAll()
AndroidView(
modifier = modifier.pointerInput(camera, focusOnTap)
if (!focusOnTap) return@pointerInput
detectTapGestures
val meteringPointFactory = SurfaceOrientedMeteringPointFactory(
size.width.toFloat(),
size.height.toFloat()
)
val meteringAction = FocusMeteringAction.Builder(
meteringPointFactory.createPoint(it.x, it.y),
FocusMeteringAction.FLAG_AF
).disableAutoCancel().build()
camera?.cameraControl?.startFocusAndMetering(meteringAction)
,
factory = _ ->
PreviewView(context).also
it.scaleType = scaleType
it.implementationMode = implementationMode
preview.setSurfaceProvider(it.surfaceProvider)
)
【讨论】:
以上是关于如何将 CameraView 与 Jetpack Compose 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
在android中的surfaceview上创建cameraview(掩码)
在 CameraView 上应用 Live GpuImage
Kotlin/Anko/OpenCV/CameraView 无法创建处理程序,因为线程尚未调用 Looper.prepare()