目前在Compose中没有CameraView(和PreviewView)的等效物。是否有可能将其包装并显示在Compose布局中?
目前在Compose中没有CameraView(和PreviewView)的等效物。是否有可能将其包装并显示在Compose布局中?
仍然没有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
?谢谢! - Victor Udeimplementation "androidx.camera:camera-camera2:1.0.0-rc04"
implementation "androidx.camera:camera-lifecycle:1.0.0-rc04"
implementation "androidx.camera:camera-view:1.0.0-alpha23"
除此之外,还有很多其他的AndroidX依赖项,但我认为这些就足够了。我想1.1.0版本也应该可以,但我还没有尝试过。 - Sean1.1.0-alpha03
版本,现在已经可以工作了,我准备回来报告。这真是个美好的时代! :)https://mvnrepository.com/artifact/androidx.camera/camera-lifecycle - Victor Ude目前CameraX还没有官方的可组合函数,因此我们必须在组合中膨胀遗留的Android视图。
要实现这一点,我们可以使用AndroidView
可组合函数,
它接受两个参数
resId
要膨胀的布局资源的ID。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_width="match_parent"
android:layout_height="match_parent" />
使用AndroidView
组合函数膨胀它。
@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()
}
}
}
这是我的片段(基于Sean的答案),它还处理了torch状态和资源配置,并专注于tap逻辑。 依赖项:
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)
}
}
)
}
我创建了一个库,用于在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)
}