I’m trying to make this diagram in Compose using a...
# compose
j
I’m trying to make this diagram in Compose using a Row and some ConstraintLayout, but this is the best I’ve been able to do so far. I’d appreciate pointers on how to fix this! Code in thread
Copy code
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ConstraintLayoutContent()
        }
    }
}

@Composable
fun Circle(size: Dp, color: Color, modifier: Modifier = Modifier) {
    Box(
        modifier = Modifier.size(size).clip(CircleShape).background(color).then(modifier)
    )
}

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        val (mercury, label) = createRefs()
        val mod = Modifier.constrainAs(mercury) {}

        Row(
            horizontalArrangement = Arrangement
                .spacedBy(space = 50.dp),
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier.padding(10.dp)
        ) {
            Circle(size = 15.dp * 2, color = Color(0xFFEBE3CF), modifier = mod)
            Circle(size = 36.dp * 2, color = Color(0xFFDC933C))
            Circle(size = 38.dp * 2, color = Color(0xFF179DD7))
            Circle(size = 21.dp * 2, color = Color(0xFFF1CF8E))
        }
        Text(
            "Mercury",
            Modifier.constrainAs(label) {
                centerHorizontallyTo(mercury)
            }
        )
        createVerticalChain(label, mercury, chainStyle = ChainStyle.Spread)
    }
}
If I remove
centerHorizontallyTo
, then the text is placed in the correct vertical position, but I think it’s always left-aligned to the screen
so e.g. if I were to move the label to a different planet then it no longer appears vertically centered. The label is positioned similar to how a tooltip might be, but I’d like to write the positioning using constraints if possible rather than a custom layout or tooltip library
r
Did you consider just using a horizontal row, with the text and Mercury in a column? Getting the planet centered vertically could be a tad interesting, but I'd think you could use equally `.weight()`ed components on either side, and put the text in one of them.
Although I suppose that might mess with the width of the Mercury column, too
j
I want the planets to be evenly spaced, but the label is wider than the planet
So if I group them, then the horizontal spacing becomes tricky I think
What does
.weight()
do? Is that flex spacing?
r
weighted items expand to take up any remaining space, proportional to their weight
👍 1
I haven't used constraint layout enough to be much help there, but this doesn't look like it would be terribly hard to do using a fully custom layout.
j
Yeah I think this is definitely possible using a fully custom layout! I was mostly wondering if it was possible using some other high level API
I guess flex with custom weights used properly might be able to do it though it doesn’t feel totally satisfying
s
Of course not sure what exactly your requirements are here, but have you considered laying out the planets just as you would normally, and then placing the text in a way that it takes 0.size in the layout, so it does not interfere with how the planets align, and then shift it a bit above the planet? This won’t do things like move the planets further from each other if the text is really long, and if there is something above that planet it might overlap with it, but it may be a start for you to tweak.
Copy code
@Preview
@Composable
private fun PreviewTest() {
  Row(
    modifier = Modifier
      .fillMaxWidth()
      .heightIn(350.dp),
    horizontalArrangement = Arrangement.SpaceEvenly,
    verticalAlignment = Alignment.CenterVertically,
  ) {
    Planet(text = "Mercury", planetRadius = 35.dp, color = Color(0xFFCCCA9D))
    Planet(text = null, planetRadius = 70.dp, color = Color(0xFFD6A101))
    Planet(text = "Uranus", planetRadius = 80.dp, color = Color(0xFF54B6FF))
    Planet(text = null, planetRadius = 40.dp, color = Color(0xFFF7CD7A))
  }
}

@Composable
fun Planet(text: String?, color: androidx.compose.ui.graphics.Color, planetRadius: Dp, modifier: Modifier = Modifier) {
  Box(modifier) {
    Box(
      modifier = modifier
        .size(planetRadius)
        .clip(CircleShape)
        .background(color = color),
    )
    if (text != null) {
      BasicText(
        text = text,
        modifier = Modifier
          .align(Alignment.TopCenter)
          .size(0.dp)
          .wrapContentSize(align = Alignment.BottomCenter, unbounded = true)
          .offset(y = -10.dp),
      )
    }
  }
}
image.png