Ojav
Ojav

Reputation: 960

How To Get The Absolute Position Of My Composable Function/Children?

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?

Edit1:

@Composable
fun PixelToDp(pixelSize: Int): Dp {
    return with(LocalDensity.current) { pixelSize.toDp() }
}

enter image description here

EDIT:2

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

Answers (1)

Thracian
Thracian

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

enter image description here

Upvotes: 9

Related Questions