How do I call methods of view in androidView? ``...
# compose
j
How do I call methods of view in androidView?
Copy code
AndroidView(
                factory = {
                    CropImageView(it).apply {
                        setCenterMoveEnabled(true)
                        setSnapRadius(16f)
                        setBackgroundColor(Color.Black.toArgb())
                        with(density) {
                            layoutParams = ViewGroup.LayoutParams(maxWidth.roundToPx(), maxHeight.roundToPx())
                        }
                    }
                },
                update = { cropView ->
                    // how to call cropView.rotateImage(90)                                    }
            )
m
you don’t. Compose is ‘reactive’. You don’t call methods, you observe changes. So, just observe a properly in your AndroidView composable, and compose will call update for you
j
sorry, but idea itself seems abstract to me. I’m not able to understand how will it be updated automatically?
this is a crop view, it just holds imageView and outside this view there’s a button which triggers rotate on this image view. so how exactly does this work?
m
just define a property and use it inside the update. Something like: val rotation by remember{ mutableStateOf(90) }
everytime you change that value, compose will automatically call ‘update’ for you
j
okay, and what about crop? say there’s method on view called cropImageView#startCrop(). In this case how will it work?
m
@Composable
fun MainComposable(){
var rotation by remember{mutableStateOf(90)}
MyAndroidView(rotation)
// somewhere around here, you have a lambda with a click
// just call rotation = otherValue
}
@Composable
fun MyAndroidView(rotation: Float){
AndroidView(
factory = {
CropImageView(it).apply {
setCenterMoveEnabled(true)
setSnapRadius(16f)
setBackgroundColor(Color.Black.toArgb())
with(density) {
layoutParams = ViewGroup.LayoutParams(maxWidth.roundToPx(), maxHeight.roundToPx())
}
}
},
update = { cropView ->
cropView.rotateImage(rotation)                                    }
)
}
j
got it but can you please demonstrate for startCrop method?
m
that is not an easy question. I’v actually never needed to do that. Let me check
I’m not sure about that. But one solution could be remembering the View, and then use effects to call methods on that view. Let me check an example
j
umm, official doc doesn’t recommend remembering the view
m
ya, i quite figured that out. But then how would you do it?
j
yeh this is big question currently I’ve.
m
You can try something like this (disclaimer, i have no idea if this would work or if it is the right approach, because if you have some unstable properties, you can get unwanted crops)
Copy code
enum class CropState {
    START,
    END,
    NONE
}

@Composable
fun MainComposable() {
    var rotation by remember {
        mutableStateOf(90f)
    }

    var scropState by remember {
        mutableStateOf(CropState.NONE)
    }

    MyAndroidView(
        rotation = rotation,
        cropState = scropState
    )
    
    // somewhere here updated rotation
    // somewhere here change your cropState
}

@Composable
fun MyAndroidView(
    modifier: Modifier = Modifier,
    rotation: Float,
    cropState: CropState
) {
    AndroidView(
        factory = { context ->
            // your view here
            View(context)
        },
        update = {
            // some property updated
            when (cropState) {
                CropState.START -> TODO()
                CropState.END -> TODO()
                CropState.NONE -> TODO()
            }
        },
        modifier = modifier
    )
}
j
okay not sure it will what if I click on rotate multiple times, will it rotate everytime as state would be same RotateState.Start _better I increment a variable on click of rotate and I listen in update -> rotateNumber.let { cropView.ro_tate(90) }
m
Initially, i would use a channel with events. But i am not sure how to handle them inside the View, because you need a scope. Again, you could remember the scope outside and use it inside (it would work i guess) - but i am not comfortable with that approach, neither with my previous one. Better wait for a more experienced response 😛
That’s actually a good question ;o
a
You can remember the view, Google Maps does this, lol 😆
j
shitttt
a
Realistically, it'd be best to keep things as event based as possible, if you want to provide actions, you could make your compose children scoped kinda like how scoped modifiers work
update runs every recomposition to update based on internal composable state
You might want to keep your rotation as an argument or inside mutableState
then for rotation buttons for example, provide events that bubble up to the parent, keeping your state hoisted
Copy code
@Composable
fun CropView(
  centerMoveEnabled: Boolean = true,
  snapRadius: Float: 16F,
  rotation: Float = 0F,
  modifier: Modifier = Modifier,
) {
  val context = LocalContext.current
  val view = remember { CropImageView(context) }

  AndroidView(
    modifier = Modifier,
    factory = { view },
    update = { view ->
      view.setCenterMoveEnabled(centerMoveEnabled)
      view.setSnapRadius(snapRadius)
      view.rotateImage(rotation)

      // or if rotate is based on a delta
      // view.rotateImage(rotation - view.rotation)
    }
  )
}
threw this together in text edit real fast
Might not be perfect, but this should do the job
j
right, got the idea. thanks man