Thread
#compose
    sindrenm

    sindrenm

    1 year ago
    How would I go about constraining the top of one composable to the (vertical) center of another composable? I don't initially know the size of any of them. Is there a way to get the size of a composable post-measure? Thanks. 😒imple_smile:
    j

    Jamie Craane

    1 year ago
    sindrenm

    sindrenm

    1 year ago
    Thanks! I managed to get it to work with the
    onSizeChanged
    modifier. No need for a
    ConstraintLayout
    , though, just adding a top padding on a box is enough. 😒imple_smile:
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    I believe using onSizeChanged will result in a frame delay, might just be simpler to make a custom layout if you don't have to manage too many other children. But yea this sounds like exactly the sort of thing constraint layout was designed for
    sindrenm

    sindrenm

    1 year ago
    If there was a way to do
    top.linkTo(otherComposable.verticalCenter)
    , then that would be best. But AFAIK there is no such thing in
    ConstraintLayout.java
    either, and the Compose stuff probably just mirrors/delegates to that.
    Wait … there is such a thing. Let me try that real quick. I must've been blind the first time. 😕
    Hmm … maybe I'm missing something, but this was harder than I thought. 😅 What I'm trying to achieve is something like the attached screenshot, which I've managed to do with this code right here:
    @Composable
    @Preview
    fun Test() {
        Box(Modifier.fillMaxSize()) {
            ConstraintLayout(Modifier.align(Alignment.Center)) {
                val (redBoxInTheBack, gradientBoxOnTop) = createRefs()
    
                Box(modifier = Modifier
                    .height(200.dp)
                    .width(200.dp)
                    .background(Color.Red)
                    .constrainAs(redBoxInTheBack) {
                        top.linkTo(<http://parent.top|parent.top>)
                        start.linkTo(parent.start)
                        end.linkTo(parent.end)
                    }
                )
    
                Box(modifier = Modifier
                    .height(50.dp)
                    .width(100.dp)
                    .background(VerticalGradient(listOf(Color.Green, Color.Blue), startY = 0f, endY = 100f))
                    .constrainAs(gradientBoxOnTop) {
                        centerAround(<http://parent.top|parent.top>)
                        start.linkTo(redBoxInTheBack.start)
                    }
                )
            }
        }
    }
    The problem I'm having is that I can constrain the center of a composable to the top of somewhere, but I can't constrain the top of something to the center of something else, if that makes sense. And so that means that if I don't have any padding around my composable, then it clips the gradient box on the top, like in the second screenshot. It clipping the top there makes sense, I guess, because the red box is supposed to be at the parent's top, but I don't see how I can put the gradient box on top and then place the red box' top on the gradient box' center. Any ideas? 🤔
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Can you post a sketch of the result you’re trying to get? I’m not sure what that is.
    Do you want the gradient box to be top-start-aligned?
    sindrenm

    sindrenm

    1 year ago
    Yeah, I want basically exactly what's in the first screenshot, but without having to explicitly add spacing around the composable itself for the gradient box to be fully visible, if that makes sense. Without a padding, the composable seems to only have the size of the red box, and thus clipping the gradient box (as you can see in the second screenshot, where only half of the gradient box is visible).
    In screenshot #1, the gradient box is fully visible only because there's space around the
    ConstraintLayout
    (because it's centered in the outer
    Box
    ). Second example is essentially the same thing, but where the outermost
    Box
    has no
    Modifier.fillMaxSize()Modifier.fillMaxSize()
    .
    j

    Jamie Craane

    1 year ago
    You might want to check a custom layout in this case. As an example, the following code:
    setContent {
                MyApplicationTheme {
                    Box(modifier = Modifier.height(200.dp).width(200.dp).background(Color.Gray)) {
                        Layout(children = {
                            Box(modifier = Modifier.height(150.dp).width(150.dp).background(Color.Red))
                            Box(modifier = Modifier.height(50.dp).width(100.dp).background(Color.Green))
                        }) { measurables, constraints ->
                            val placeables = measurables.map { measurable ->
                                measurable.measure(constraints)
                            }
    
                            layout(constraints.maxWidth, constraints.maxHeight) {
                                val second = placeables[1]
                                val first = placeables.first()
                                first.placeRelative(0, second.height / 2)
                                second.placeRelative(0, 0)
                            }
                        }
                    }
                }
            }
    produces the result as in the attached screenshot. You might need to tune the code because it always assumed two composables exists.
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Yea, I would probably just use a custom layout. Seems like ConstraintLayout should be able to handle this, but the DSL doesn’t support all the CL features yet?
    j

    Jamie Craane

    1 year ago
    I am not sure if ConstraintLayout supports this, I do not see a relative positioning to position a view relative to the center of another view: https://developer.android.com/reference/androidx/constraintlayout/widget/ConstraintLayout#RelativePositioning
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    Couple notes about that code snippet though, you need to adjust the constraints to account for your extra height, and the dimensions passed to
    layout()
    should be the actual dimensions after measuring. E.g.:
    j

    Jamie Craane

    1 year ago
    Yes, that is correct! (it really was a quick and dirty mockup of how this could be used). Nice additions!
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    To do this with XML CL, I think you’d need to create a guide/barrier or something that is centered in the badge, and then align the top of the main one to that?
    j

    Jamie Craane

    1 year ago
    I don’t think placement order changes the z-index. It seems placements of the children affects z-order. It might be handy if one can explicitly define (an optional) z-order when placing a placeable.
    sindrenm

    sindrenm

    1 year ago
    Oh, nice! Thanks a ton, folks, I wasn't aware of
    Layout
    , but that's definitely the way to go in this case. Also, thanks for pointing out the needed size adjustments. Makes sense. 👏 ❤️
    I was looking for a way to put a guideline in the vertical center of the badge there, but I couldn't find a way to do just that. Not sure if you can in XML either, though? Not that it matters, Compose all the way, d00ds. 😎
    Zach Klippenstein (he/him) [MOD]

    Zach Klippenstein (he/him) [MOD]

    1 year ago
    There was a recent CL to make placement order affect drawing order, but I don’t think it made it into alpha13. https://android-review.googlesource.com/c/platform/frameworks/support/+/1435797
    j

    Jamie Craane

    1 year ago
    Good to know!