Reputation: 960
I have tried to use this answer where he uses OnGloballyPositionedModifier
but it is not returning the correct absolute Pos of my children.I tried positionInWindow()
, positionInRoot()
. It appears to work when the column
does the spacing but not when I do it with offset
.
@Composable
fun GetAbsolutePos(
content: @Composable () -> Unit,
) {
Box(modifier = Modifier
.onGloballyPositioned { coordinates ->
println("My coordinates: " + coordinates.positionInRoot())
}
) {
content()
}
}
@Preview
@Composable
fun test() {
GetAbsolutePos() {
Box(
modifier = Modifier
.width(PixelToDp(pixelSize = 200))
.offset(x = PixelToDp(pixelSize = 100), y = PixelToDp(pixelSize = 50))
.height(PixelToDp(pixelSize = 200))
.background(Color.Red)
) {
GetAbsolutePos() {
Column(
) {
GetAbsolutePos() {
Box(
Modifier
.size(PixelToDp(pixelSize = 20))
.background(Color.Green)
)
}
GetAbsolutePos() {
Box(
Modifier
.size(PixelToDp(pixelSize = 20))
.background(Color.Blue)
)
}
}
}
}
}
}
which prints the following
I/System.out: My coordinates: Offset(0.0, 0.0) // wrong I used offset, it should be 100x50
I/System.out: My coordinates: Offset(100.0, 50.0) // correct
I/System.out: My coordinates: Offset(100.0, 50.0) // correct
I/System.out: My coordinates: Offset(100.0, 70.0) // correct? so it works on column spacing but not with offset????
Is there a way to get the Absolute Position of my composable function in relation to the whole ANDROID WINDOW?
@Composable
fun PixelToDp(pixelSize: Int): Dp {
return with(LocalDensity.current) { pixelSize.toDp() }
}
The order of modifier should not matter since the function onGloballyPositioned
states that.
This callback will be invoked at least once when the LayoutCoordinates are available, and every time the element's position changes within the window.
The same is true for onSizeChanged
and can be observed by running the code below (same example as in the answer by @Thracian.)
Invoked with the size of the modified Compose UI element when the element is first measured or when the size of the element changes.
Since the function is literally a callback the order shouldn't matter. If im wrong with this pls feel free to correct me.
Box(modifier = Modifier
.onSizeChanged {
println("🔥 Size 1: $it")
}
.size(PixelToDp(pixelSize = 150))
.onSizeChanged {
println("🔥 Size 2: $it")
}
.background(Color.Red)
)
Prints
I/System.out: 🔥 Size 2: 150 x 150
I/System.out: 🔥 Size 1: 150 x 150
Upvotes: 1
Views: 3403
Reputation: 66516
You are not taking order of modifiers matter into consideration when you check position of your Composables
Box(modifier = Modifier
.onGloballyPositioned {
val positionInRoot = it.positionInRoot()
println("🔥 POSITION 1: $positionInRoot")
}
.offset {
IntOffset(100,50)
}
.onGloballyPositioned {
val positionInRoot = it.positionInRoot()
println("🔥🔥 POSITION 2: $positionInRoot")
}
.size(100.dp)
.background(Color.Red)
)
Prints:
🔥 POSITION 1: Offset(0.0, 0.0)
🔥🔥 POSITION 2: Offset(100.0, 50.0)
In this answer i explained how order of modifiers change result for some of the modifiers including Modifier.offset
https://stackoverflow.com/a/74145347/5457853
I made an example to show interaction with siblings, other modifiers and position based on how modifiers applied agains Modifier.offset or Modifier.graphicsLayer
@Composable
private fun OffsetAndTranslationExample() {
val context = LocalContext.current
var value by remember { mutableStateOf(0f) }
var positionBeforeOffset by remember {
mutableStateOf("Offset.Zero")
}
var positionAfterOffset by remember {
mutableStateOf("Offset.Zero")
}
Row(modifier = Modifier.border(2.dp, Color.Red)) {
Box(
modifier = Modifier
.onGloballyPositioned {
positionBeforeOffset = "Position before offset: ${it.positionInRoot()}"
}
.offset {
IntOffset(value.toInt(), 0)
}
.onGloballyPositioned {
positionAfterOffset = "Position after offset: ${it.positionInRoot()}"
}
.clickable {
Toast
.makeText(context, "One with offset is clicked", Toast.LENGTH_SHORT)
.show()
}
.zIndex(2f)
.shadow(2.dp)
.border(2.dp, Color.Green)
.background(Orange400)
.size(120.dp)
)
Box(
modifier = Modifier
.zIndex(1f)
.background(Blue400)
.size(120.dp)
.clickable {
Toast
.makeText(context, "Static composable is clicked", Toast.LENGTH_SHORT)
.show()
}
)
}
Spacer(modifier = Modifier.height(40.dp))
Row(modifier = Modifier.border(2.dp, Color.Red)) {
Box(
modifier = Modifier
.graphicsLayer {
translationX = value
}
.clickable {
Toast
.makeText(context, "One with graphicsLayer is clicked", Toast.LENGTH_SHORT)
.show()
}
.zIndex(2f)
.shadow(2.dp)
.border(2.dp, Color.Green)
.background(Orange400)
.size(120.dp)
)
Box(
modifier = Modifier
.zIndex(1f)
.background(Blue400)
.size(120.dp)
.clickable {
Toast
.makeText(context, "Static composable is clicked", Toast.LENGTH_SHORT)
.show()
}
)
}
Spacer(modifier = Modifier.height(5.dp))
Text("Offset/Translation: ${value.round2Digits()}")
Slider(
value = value,
onValueChange = {
value = it
},
valueRange = 0f..1000f
)
Text(positionBeforeOffset)
Text(positionAfterOffset)
}
As can be seen changing offset of Orange Composable doesn't effect position of Blue Composable but it changes where touch position is bounded and global position changes before and after offset is applied
Upvotes: 9