I'm trying to set a touch listener to my CameraX preview - ultimately to set the focus but I can't get the touch listener to trigger. My preview is created in a composable and I'm not sure if I am referencing it correctly when I setOnTouchListener(). Should I be using PreviewView - if not how do I get the reference to the preview?
private val handleTouch = OnTouchListener { v, event ->
val x = event.x.toInt()
val y = event.y.toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> Log.i("Touch", "touched down")
MotionEvent.ACTION_MOVE -> Log.i("Touch", "moving: ($x, $y)")
MotionEvent.ACTION_UP -> Log.i("Touch", "touched up")
}
true
}
PreviewView.setOnTouchListener(handleTouch)
}@Composable
fun CameraPreview(
controller: LifecycleCameraController,
modifier: Modifier = Modifier
) {
val lifecycleOwner = LocalLifecycleOwner.current
// select the highest resolution available
val screenSize = Size(9, 12)
val resolutionSelector = ResolutionSelector.Builder()
.setResolutionStrategy(ResolutionStrategy(screenSize, FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER))
.setAspectRatioStrategy(RATIO_4_3_FALLBACK_AUTO_STRATEGY)
.build()
AndroidView(
factory = {
PreviewView(it).apply {
this.controller = controller
controller.bindToLifecycle(lifecycleOwner)
//controller.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
controller.previewResolutionSelector = resolutionSelector
}
},
modifier = modifier
)
}
I'm trying to set a touch listener to my CameraX preview - ultimately to set the focus but I can't get the touch listener to trigger. My preview is created in a composable and I'm not sure if I am referencing it correctly when I setOnTouchListener(). Should I be using PreviewView - if not how do I get the reference to the preview?
private val handleTouch = OnTouchListener { v, event ->
val x = event.x.toInt()
val y = event.y.toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> Log.i("Touch", "touched down")
MotionEvent.ACTION_MOVE -> Log.i("Touch", "moving: ($x, $y)")
MotionEvent.ACTION_UP -> Log.i("Touch", "touched up")
}
true
}
PreviewView.setOnTouchListener(handleTouch)
}@Composable
fun CameraPreview(
controller: LifecycleCameraController,
modifier: Modifier = Modifier
) {
val lifecycleOwner = LocalLifecycleOwner.current
// select the highest resolution available
val screenSize = Size(9, 12)
val resolutionSelector = ResolutionSelector.Builder()
.setResolutionStrategy(ResolutionStrategy(screenSize, FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER))
.setAspectRatioStrategy(RATIO_4_3_FALLBACK_AUTO_STRATEGY)
.build()
AndroidView(
factory = {
PreviewView(it).apply {
this.controller = controller
controller.bindToLifecycle(lifecycleOwner)
//controller.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
controller.previewResolutionSelector = resolutionSelector
}
},
modifier = modifier
)
}
Share
Improve this question
edited Mar 21 at 15:28
garrettlynchirl
asked Mar 20 at 18:31
garrettlynchirlgarrettlynchirl
91911 silver badges26 bronze badges
4
|
2 Answers
Reset to default 0You also need a Preview builder that I did not see in your code:
Here is the correct version:
private val handleTouch = OnTouchListener { v, event ->
val x = event.x.toInt()
val y = event.y.toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> Log.i("Touch", "touched down")
MotionEvent.ACTION_MOVE -> Log.i("Touch", "moving: ($x, $y)")
MotionEvent.ACTION_UP -> {
Log.i("Touch", "touched up")
v.performClick() // Call performClick when ACTION_UP
}
}
true
}
// Camera preview
@OptIn(ExperimentalCamera2Interop::class)
@Composable
fun CameraPreview() {
val lensFacing = CameraSelector.LENS_FACING_BACK
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
//Notice this two variables
val preview = androidx.camera.core.Preview.Builder().build()
val previewView = remember {
PreviewView(context)
}
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(lensFacing)
.build()
val cameraProviderFuture = remember {
ProcessCameraProvider.getInstance(context)
}
LaunchedEffect(key1 = Unit) {
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
val camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
cameraSelector,
preview
)
preview.setSurfaceProvider(previewView.surfaceProvider) // You need to set the provider
previewView.setOnTouchListener(handleTouch) // Here you can set the onTouch
}, ContextCompat.getMainExecutor(context))
}
// Preview
Box(
modifier = Modifier
.fillMaxSize()
.background(
Color.Black
)
) {
AndroidView(
factory = {
previewView
},
modifier = Modifier.fillMaxSize()
)
}
}
If you need to access the PreviewView globaly just create a global var and then initialized in the CameraPreview
@Shadman Adman - This is what I've understood from your comment but clearly it's wrong (it now crashes my app). The issue seems to be with previewView. I'm just not understanding how it's passed to the composable. To save time you could just edit this answer to point out what I'm doing wrong.
class MainActivity : ComponentActivity() {
private lateinit var controller: LifecycleCameraController
// You do not need this if you don't need to access it globalyl
var previewView = null // is this correct?
setContent {
CameraApp1Theme {
// ignore all this - just putting it here as the variables are referenced
val resolutionSelector = ResolutionSelector.Builder()
.setResolutionStrategy(HIGHEST_AVAILABLE_STRATEGY)
.setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY)
.build()
controller = remember {
LifecycleCameraController(applicationContext).apply {
setEnabledUseCases(CameraController.IMAGE_CAPTURE)
setImageCaptureMode(CAPTURE_MODE_MAXIMIZE_QUALITY)
setImageCaptureResolutionSelector(resolutionSelector)
}
}
// ignore all this - just putting it here as the variables are referenced
CameraPreview(
controller = controller,
modifier = Modifier
.align(Alignment.Center)
.size(pwidth.dp, pheight.dp)
)
}
}
}
@Composable
fun CameraPreview(
controller: LifecycleCameraController,
modifier: Modifier = Modifier
) {
val lifecycleOwner = LocalLifecycleOwner.current
val context = LocalContext.current
val previewView = remember {
PreviewView(context)
}
val screenSize = Size(9, 12)
val resolutionSelector = ResolutionSelector.Builder()
.setResolutionStrategy(ResolutionStrategy(screenSize, FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER))
.setAspectRatioStrategy(RATIO_4_3_FALLBACK_AUTO_STRATEGY)
.build()
AndroidView(
factory = {
previewView.apply {
this.controller = controller
controller.bindToLifecycle(lifecycleOwner)
controller.previewResolutionSelector = resolutionSelector
}
},
modifier = modifier
)
previewView.setOnTouchListener(handleTouch) // here?
}
PreviewView.
Create a globalpreviewView
variable and set it to null. Then define it inCameraPreview
and attach thehandleTouch
– Shadman Adman Commented Mar 21 at 11:36previewView = PreviewView(it) ....
After theAndroidView
part set the touch:PreviewView.setOnTouchListener(handleTouch)
I will create an answer if it's still unclear – Shadman Adman Commented Mar 21 at 19:09