我正在使用cameraX 库实现自定义相机。我正在努力集中注意力,我做了以下事情
viewFinder.setOnTouchListener { v, event ->
return@setOnTouchListener when (event.action) {
MotionEvent.ACTION_DOWN -> {
true
}
MotionEvent.ACTION_UP -> {
// The below code is for focusing
val factory = SurfaceOrientedMeteringPointFactory(
viewFinder.width.toFloat(),
viewFinder.height.toFloat()
)
val point = factory.createPoint(event.x, event.y)
try {
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
.apply {
disableAutoCancel() //focus only when the user tap the preview
}.build()
val future = cameraControl?.startFocusAndMetering(action)
future?.addListener(Runnable {
--->line 66 val result = future?.get()
println("log result ---> $result")
}, cameraExecutor)
} catch (e: CameraInfoUnavailableException) {
println("log error ---> $e")
}
true
}
else -> false // Unhandled event.
}
}
现在,它是一些聚焦我所触及的点的东西,它不是太准确,但还可以。当聚焦 5 到 6 次后,应用程序崩溃并关闭。
FATAL EXCEPTION: pool-2-thread-1
...
...
java.lang.Error: java.util.concurrent.ExecutionException: androidx.camera.core.CameraControl$OperationCanceledException: Cancelled by another startFocusAndMetering()
...
...
...MainActivity$onCreate$2$1.run(MainActivity.kt:66)
我不明白为什么它会崩溃,而且它并不经常崩溃。有时会崩溃,有时不会。
编辑1:-
我知道当我多次点击焦点时就会发生这种情况。因此,在开始对焦之前,我需要清除之前的焦点(如果正在进行)。但是,如何清除之前的焦点我不明白。
编辑2:-
我尝试了这个
cameraControl?.cancelFocusAndMetering()
,我只是将它放在 try 块的第一行。但仍然存在问题。
对这里的java代码表示歉意。但根据我的经验,future?.get() 需要包含在 try, catch 中
future.addListener(() -> {
try
{
FocusMeteringResult result = (FocusMeteringResult) future.get();
if(result.isFocusSuccessful())
{
// Focus has succeeded
}
else
{
// Focus has failed
}
}
catch (ExecutionException e) // Thrown exceptions
{
e.printStackTrace();
}
catch (InterruptedException e) // Thrown exceptions
{
e.printStackTrace();
}
}
我刚刚删除了这个块
future?.addListener(Runnable {
val result = future?.get()
println("log result ---> $result")
}, cameraExecutor)
并且运行良好。但我不知道这是对还是错。
有人知道吗?请回答。目前我将其标记为答案,因为它有效。
正如您提到的,问题是由于 future?.addListener 造成的。为了解决这个问题,你可以在 viewFinder.setOnTouchListener 之外定义 future 变量:
private var future: ListenableFuture<FocusMeteringResult>? = null
在调用 startFocusAndMetering(action) 之前取消 future
future?.cancel(true)
所以你的最终代码看起来像这样:
private var future: ListenableFuture<FocusMeteringResult>? = null
viewFinder.setOnTouchListener { v, event ->
return@setOnTouchListener when (event.action) {
MotionEvent.ACTION_DOWN -> {
true
}
MotionEvent.ACTION_UP -> {
future?.cancel(true)
// The below code is for focusing
val factory = SurfaceOrientedMeteringPointFactory(
viewFinder.width.toFloat(),
viewFinder.height.toFloat()
)
val point = factory.createPoint(event.x, event.y)
try {
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
.apply {
disableAutoCancel() //focus only when the user tap the preview
}.build()
future = cameraControl?.startFocusAndMetering(action)
future?.addListener(Runnable {
--->line 66 val result = future?.get()
println("log result ---> $result")
}, cameraExecutor)
} catch (e: CameraInfoUnavailableException) {
println("log error ---> $e")
}
true
}
else -> false // Unhandled event.
}
}
我结合了上述解决方案来阻止 android compose 中的双击崩溃...
在可组合项中,
var future: ListenableFuture<FocusMeteringResult>? = null
然后先取消future,之后调用focus设置future:
previewView.setOnTouchListener { _, event ->
future?.cancel(true)
isFocusVisible = true
if (event.action == MotionEvent.ACTION_UP) {
camera?.let {
focusPoint = Offset(event.x, event.y)
val factory: MeteringPointFactory = previewView.meteringPointFactory
val point: MeteringPoint = factory.createPoint(event.x, event.y)
val action = FocusMeteringAction.Builder(point).build()
future = camera?.cameraControl?.startFocusAndMetering(action)
isFocusVisible = false
}
}
true
}
对我有用,不需要任何 try-catch 逻辑。