Skip to content

Instantly share code, notes, and snippets.

@thebino
Last active August 8, 2022 06:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thebino/2d7385d5b3e51c036fa1873bac7b7ac7 to your computer and use it in GitHub Desktop.
Save thebino/2d7385d5b3e51c036fa1873bac7b7ac7 to your computer and use it in GitHub Desktop.
Intrinsics layout to constrain a row by width
package com.example
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Content()
}
}
}
@Preview(name = "Content")
@Composable
fun Content() {
Column(Modifier.padding(8.dp)) {
Text(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp),
fontStyle = FontStyle.Italic,
text = "1 to 10 with Row"
)
Row(modifier = Modifier.padding(horizontal = 8.dp)) {
for (i in 1..10) {
Chip(
modifier = Modifier.padding(4.dp),
text = "Chip $i"
)
}
}
Text(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp),
fontStyle = FontStyle.Italic,
text = "1 to 10 with LazyRow"
)
LazyRow(modifier = Modifier.padding(horizontal = 8.dp)) {
items(9) {
Chip(
modifier = Modifier.padding(4.dp),
text = "Chip $it"
)
}
}
Text(
modifier = Modifier.padding(horizontal = 8.dp, vertical = 16.dp),
fontStyle = FontStyle.Italic,
text = "1 to 10 with MaxWidth"
)
MaxWidthRow(modifier = Modifier.padding(horizontal = 8.dp)) {
for (i in 1..10) {
Chip(
modifier = Modifier.padding(4.dp),
text = "Chip $i"
)
}
}
}
}
@Composable
private fun MaxWidthRow(
modifier: Modifier = Modifier,
children: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = children,
) { measurables, outerConstraints ->
val rows = mutableListOf<List<Placeable>>()
val currentRow = mutableListOf<Placeable>()
// iterate through all children to get their size
for (measurable in measurables) {
val childConstraints = Constraints(maxWidth = outerConstraints.maxWidth)
val childPlaceable = measurable.measure(childConstraints)
fun itemFitInCurrentLine(): Boolean {
return currentRow.sumOf { it.width } + childPlaceable.width <= outerConstraints.maxWidth
}
// add to currentRow if it fits
if (currentRow.isEmpty() || itemFitInCurrentLine()) {
currentRow += childPlaceable
if (rows.isEmpty()) {
rows += currentRow
}
} else {
// save current row
rows += currentRow.toList()
// start over with this row
currentRow.clear()
currentRow += childPlaceable
}
}
val calculatedHeight = if (rows.isNotEmpty() && rows.first().isNotEmpty()) {
val itemHeight = rows.first().first().height
rows.size * itemHeight
} else {
0
}
layout(
width = outerConstraints.maxWidth,
height = calculatedHeight
) {
rows.reversed().forEachIndexed { rowIndex, placeables ->
placeables.forEachIndexed { itemIndex, childPlaceable ->
val widthOfPlacedItems = mutableListOf<Int>()
for (i in 0 until itemIndex) {
widthOfPlacedItems += placeables[i].width
}
val x = if (widthOfPlacedItems.sum() > 0) {
widthOfPlacedItems.sum() + 12.dp.toPx().toInt()
} else {
widthOfPlacedItems.sum()
}
val y = (rowIndex) * childPlaceable.height
childPlaceable.place(x = x, y = y)
}
}
}
}
}
/**
* Chip like element to represent options
*/
@Composable
fun Chip(
modifier: Modifier = Modifier,
text: String,
selected: Boolean = false,
) {
Surface(
color = when {
selected -> MaterialTheme.colors.onSurface
else -> Color.Transparent
}, contentColor = when {
selected -> MaterialTheme.colors.onPrimary
else -> Color.LightGray
}, shape = CircleShape, border = BorderStroke(
width = 1.dp, color = when {
selected -> MaterialTheme.colors.primary
else -> Color.LightGray
}
), modifier = modifier
) {
// Add text to show the data that we passed
Text(
text = text,
textAlign = TextAlign.Center,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(8.dp)
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment