I've created a constraint layout
ConstraintLayout(
Modifier
.fillMaxWidth()
.wrapContentHeight()
) {
Image(
painter = rememberImagePainter(it.photoUrl.toString()),
modifier = Modifier
.constrainAs(photo) {
start.linkTo(parent.start, margin = 30.dp)
top.linkTo(parent.top, margin = 30.dp)
}
.width(130.dp)
)
Column(
modifier = Modifier
.constrainAs(holder) {
start.linkTo(photo.end, margin = 30.dp)
top.linkTo(photo.top)
bottom.linkTo(photo.bottom)
end.linkTo(parent.end, margin = 30.dp)
}
.fillMaxWidth()
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
text = "Lorem Ipsu auto text ator..."
)
}
As you can see long text can not fit in layout. How Can I wrap text to next line? I tried to change wrapContentWidht() to fillMaxParentWidth but id didn't help.
You can use width = Dimension.fillToConstraints to limit the width to the available space.
within the constrainAs block
In your example:
.constrainAs(holder) {
start.linkTo(photo.end, margin = 30.dp)
top.linkTo(photo.top)
bottom.linkTo(photo.bottom)
end.linkTo(parent.end, margin = 30.dp)
width = Dimension.fillToConstraints
}
Related
When creating an Android app, I put some Composables in a Row of a Card, as shown below, and it did not work as I expected. The Composable that I put "weight(1f)" on was no longer showing up.
data class Test(
val title: String,
val text: String
)
#Composable
fun CardRowSample(
modifier: Modifier = Modifier,
) {
val testList =
listOf(
Test("AAAA", "1,2,3,4,5,6,7,8,9,10"),
Test("BBBB", "11,12,13,14,15,16,17,18,19,20")
)
LazyColumn(
modifier = modifier
) {
items(
items = testList
) {
test ->
Card(
elevation = 12.dp,
backgroundColor = Color.LightGray,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 50.dp)
.width(40.dp)
.requiredHeight(intrinsicSize = IntrinsicSize.Min)
.padding(
horizontal = 20.dp,
vertical = 20.dp
)
.border(
width = 1.dp,
color = Color.Black,
shape = RectangleShape
)
) {
Row(
modifier = Modifier.fillMaxWidth()
) {
Icon(
modifier = Modifier
.padding(horizontal = 5.dp)
.align(Alignment.CenterVertically),
imageVector = Icons.Filled.Check,
contentDescription = null
)
Text(
text = test.title,
fontSize = 20.sp,
modifier =
Modifier
.width(120.dp)
.padding(horizontal = 10.dp, vertical = 10.dp)
)
Text(
text = test.text,
fontSize = 20.sp,
modifier = Modifier
.weight(1f)//it doesn't work!!
.padding(horizontal = 10.dp, vertical = 10.dp)
)
}
}
}
}
}
My ideal image of the layout:
I wrote the code referring to the following question, Weights in Jetpack compose, but I can't figure out why this is happening. I put "weight" on everything and added fillParentMaxSize to the Row's modifier, but I wasn't able to solve this problem.
What should I do next to solve this problem?
Problem is not with Modifier.weight(1f). It's because of Modifier.requiredHeight(IntrinsicSize.Min)
Card(
elevation = 12.dp,
backgroundColor = Color.LightGray,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 50.dp)
// .width(40.dp)
// .requiredHeight(intrinsicSize = IntrinsicSize.Min)
.padding(
horizontal = 20.dp,
vertical = 20.dp
)
.border(
width = 1.dp,
color = Color.Black,
shape = RectangleShape
)
)
Also Modifier.width(40.dp) is redundant because you have another width modifier Modifier.fillMaxWidth() before that.
When Modifier.requiredIn with Intrinsic size modifiers act strange. Composable gets measured twice and with Infinity constraints
LAYOUT constraints: Constraints(minWidth = 0, maxWidth = 1080, minHeight = 0, maxHeight = Infinity), width: 1080, height: 235
LAYOUT constraints: Constraints(minWidth = 0, maxWidth = Infinity, minHeight = 235, maxHeight = 235), width: 510, height: 235
That's the second measurement breaks width because of maxWidth = Infinity. 1080px is full width on my device
to see Constrains and Composable width and height you can use
Card(
elevation = 12.dp,
backgroundColor = Color.LightGray,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 50.dp)
// .width(40.dp)
.requiredHeight(intrinsicSize = IntrinsicSize.Min)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
println("LAYOUT constraints: $constraints, width: ${placeable.width}, height: ${placeable.height}")
layout(placeable.width,placeable.height){
placeable.placeRelative(0,0)
}
}
.padding(
horizontal = 20.dp,
vertical = 20.dp
)
.border(
width = 1.dp,
color = Color.Black,
shape = RectangleShape
)
)
An alternative to the Thracian's answer is to apply Modifier.height(IntrinsicSize.Min) to your Card and fillMaxHeight() to the Icon.
Card(
elevation = 12.dp,
backgroundColor = Color.LightGray,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 50.dp)
.height(intrinsicSize = IntrinsicSize.Min)
.padding(
horizontal = 20.dp,
vertical = 20.dp
)
.border(
width = 1.dp,
color = Color.Black,
shape = RectangleShape
)
) {
Row(
modifier = Modifier.fillMaxWidth()
) {
Icon(
modifier = Modifier
.fillMaxHeight()
.padding(horizontal = 5.dp)
.align(Alignment.CenterVertically),
imageVector = Icons.Filled.Check,
contentDescription = null
)
Text(
text = test.title,
fontSize = 20.sp,
modifier =
Modifier
.width(120.dp)
.padding(horizontal = 10.dp, vertical = 10.dp)
)
Text(
text = test.text,
fontSize = 20.sp,
modifier = Modifier
.weight(1f)
.padding(horizontal = 10.dp, vertical = 10.dp)
)
}
}
}
I'm trying to overlap two Box or perhaps is better to use Row on this case.
My design is one Row overlapped with another one, and I've wrapped it on a Column, is that correct?
This is the design, what I'd like to have is the rectangle of the top be the same size of the one below and then move it some pixels as you can see in the image, but they should have the same width but not the same height.
Is it okay if the hierarchy is :
Column
Box (the one of the top)
Row
Box (the one of the bottom)
Row (inside there is text and it's all the same align)
......
I've faced with this some days ago and I solved it using ConstraintLayout.
What I had to do is :
Add implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02" to build.gradle
Wrap every Box in a ConstraintLayout { .. }
Inside each Box add a Modifier.constrainAs to align the Top Bottom Start End as you want.
If you want the first box be the same width as the second one without hardcoding the dps you should use width = Dimension.fillToConstraints
fillToConstraints - the layout will expand to fill the space defined by its constraints in that dimension.
Basic example without hard-coding :
ConstraintLayout() {
val (title, description) = createRefs()
Box(
modifier = Modifier
.padding(start = 28.dp)
.background(color = Red)
.padding(
horizontal = 16.dp,
)
.constrainAs(title) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
width = Dimension.fillToConstraints
}
) {
Text(text = "Hello World")
}
Box(
modifier = Modifier
.padding(end = 4.dp)
.background(Color.Magenta)
.padding(bottom = 5.dp, start = 8.dp, end = 16.dp, top = 4.dp)
.constrainAs(description) {
top.linkTo(title.top, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) {
Text(text = "Skizo-ozᴉʞS rules")
}
}
Now you have to play with the padding according to your UI and adapt it, result is something like this :
This is way using BoxWithConstraints and not using fixed width and height:
BoxWithConstraints(
Modifier
.background(color = Color.Blue)
.padding(20.dp)) {
val boxWidth = this.maxWidth
Box(
modifier = Modifier
.width(boxWidth - 10.dp)
.background(Color.Red)
) {
Text(text = "Hello Android")
}
Column() {
Spacer(modifier = Modifier
.height(10.dp)
.width(10.dp))
Row( ) {
Spacer(modifier = Modifier.width(10.dp))
Box(
modifier = Modifier
.width(boxWidth)
.zIndex(2f)
.background(Color.Yellow)
) {
Text("aa", modifier = Modifier.background(color = Color.Green))
}
}
}
}
In order for the Composables to overlap, you should put them in the same Box. Try this out:
Box(modifier = Modifier.size(width = 300.dp, height = 100.dp)) {
Row(modifier = Modifier
.size(width = 200.dp, height = 50.dp)
.background(color = Color.Blue)
.align(Alignment.TopEnd)) {}
Row(modifier = Modifier
.size(width = 200.dp, height = 70.dp)
.background(color = Color.Red)
.align(Alignment.BottomStart)) {}
}
You can achieve this in many ways,
#Composable
fun BoxOverBox() {
Box(
modifier = Modifier.fillMaxSize()
.background(Color.White),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.width(200.dp)
.height(50.dp)
.background(Color.Red)
)
Box(
modifier = Modifier
.width(200.dp)
.height(50.dp)
.zIndex(2f)
.graphicsLayer {
translationX = -50f
translationY = 50f
}
.background(Color.Blue)
)
}
}
I think you must use "matchParentSize" modifier that is avaliabele inside BoxScope, I mean inside Box composable, that modifer measure other children size except itself when it has join the composable at first time to apply the same size to itself.
you can see this modifier in documentation
https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/BoxScope#(androidx.compose.ui.Modifier).matchParentSize()
I need to do something like this:
It's 10 circles views that have same width that depends on parent width.
That I have now:
Column(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White)
.padding(vertical = 12.dp)
) {
Row(
modifier = Modifier
.padding(horizontal = 20.dp)
.padding(top = 12.dp)
) {
for (i in 1..10) {
Surface(
shape = CircleShape,
modifier = Modifier
.padding(horizontal = 2.dp)
.width(0.dp)
.height(29.dp)
.weight(1F)
.clip(CircleShape),
color = surfaceColor,
onClick = { selected = i },
) {
Text(
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
text = "$i",
color = textColor,
style = subtitle1(),
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
In this case I can set same width of circles by settings Surface weight by 1F, but I need to set height of this circles that depends on width. I'm stuck how to do this.
You can use the aspectRatio modifier.
Apply to the ratio attribute your expected value.
Surface(
shape = CircleShape,
modifier = Modifier
.padding(horizontal = 2.dp)
.width(0.dp)
.weight(1F)
.aspectRatio(1f)
I need the image and text view to be positioned to the start of the card, with the columns children aligned center horizontally, however as you can see from the photo the column is somehow being stretched the width of the screen which is putting the image and text views in the center. Can anyone see any issues with my code on this?
#Composable
fun TrainerCard(profile: TrainerProfile) {
Card(modifier = Modifier
.height(180.dp)
.fillMaxWidth()
.padding(4.dp)) {
Column(modifier = Modifier
.fillMaxHeight()
.width(120.dp)
.border(1.dp, color = Color.Red),
horizontalAlignment = Alignment.CenterHorizontally) {
Image(
modifier = Modifier
.size(120.dp)
.padding(top = 4.dp, start = 4.dp),
painter = painterResource(R.drawable.pokepals_logo),
contentDescription = null
)
Text(
text = profile.trainerName,
style = MaterialTheme.typography.subtitle1)
}
}
}
In your Column add the wrapContentWidth(Alignment.Start) modifier:
Column(modifier = Modifier
.fillMaxHeight()
.width(120.dp)
.wrapContentWidth(Alignment.Start)
.border(1.dp, color = Color.Red),
horizontalAlignment = Alignment.CenterHorizontally
)
I'm trying to use the new Jetpack Compose UI framework, but I'm running into an issue. I'd like to achieve this layout, which in xml is pretty easy to achieve:
But I can't figure out how to make the vertical divider take up the available vertical space, without specifying a fixed height. This code that I've tried doesn't seem to work:
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = item.name
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I keep getting this result:
I also tried with ConstraintLayout, but it still didn't work
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
.constrainAs(divider){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(column.end)
}
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
But nothing seems to work. Is this a bug, a missing feature or am I just missing something?
EDIT: Apparently the real problem is that the divider doesn't know how to measure when the Surface doesn't have a fixed height, setting height equal to some number solves the issue, but then the view doesn't adapt to the content height anymore, so this can't be the solution
You can apply:
the modifier .height(IntrinsicSize.Max) to the Row
the modifiers .width(1.dp).fillMaxHeight() to the Spacer
You can read more about the Intrinsic measurements here.
Something like:
Row(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
....
) {
Text(text = "....")
}
Spacer(
modifier = Modifier
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(...)
}
You can set Intrinsic.Max for the preferredHeight of the Row, then set the Spacer to fill max height. You can read more on Intrinsics in this codelab section.
#Composable
fun ListItem() {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth().preferredHeight(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = "2456")
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.fillMaxHeight()
.background(color = Color.Black.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = "Some name"
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I've solved it using constraint layout:
Box(modifier = Modifier.padding(Dp(50f))) {
ConstraintLayout(
modifier = Modifier
.border(width = Dp(1f), color = Color.Black)
.fillMaxWidth()
) {
val (left, divider, right) = createRefs()
Column(
modifier = Modifier
.padding(horizontal = Dp(20f))
.constrainAs(left) {
width = Dimension.wrapContent
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(divider.start)
bottom.linkTo(parent.bottom)
}
) {
Text(text = "Code")
Text(text = "A12")
}
Box(
modifier = Modifier
.width(Dp(1f))
.background(Color.Black)
.constrainAs(divider) {
width = Dimension.wrapContent
height = Dimension.fillToConstraints
start.linkTo(left.end)
top.linkTo(parent.top)
end.linkTo(right.start)
bottom.linkTo(parent.bottom)
}
)
Box(
modifier = Modifier
.constrainAs(right) {
width = Dimension.fillToConstraints
start.linkTo(divider.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) {
Text(
text = "Test",
modifier = Modifier
.padding(vertical = Dp(100f))
.align(Alignment.Center)
)
}
}
}
The key part is using that modifier height = Dimension.fillToConstraints
There are plenty of solutions here, but I thought I could demonstrate the ConstraintLayout approach and add a helpful usage of the IntrinsicSize enum that solves one of the issues (needing an adaptive height for the composable). Interestingly, either IntrinsicSize.Max or IntrinsicSize.Min will yield the desired behavior.
I used most of your code. The key differences are:
declares a guideline (my value passed in for the fraction does not produce the exact result you were looking for, but can be adjusted easily (use a fraction slightly smaller than .2) This can be useful if you expect wrapContent to alter your left Text to vary the location of a spacer, but would prefer a consistent spacer location across a list of these items.
others have mentioned, spacer modifier should include .fillMaxHeight()
define the height of the surface wrapper to be .height(IntrinsicSize.Min) docs ref here: https://developer.android.com/jetpack/compose/layout#intrinsic-measurements
divider start is constrained to the guideline
had to change the Spacer modifier to access the width, instead of preferredWidth
#Composable
fun ListItem(item: Plate) {
Surface(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val guideline = createGuidelineFromStart(0.2f)
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(guideline)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.constrainAs(divider){
top.linkTo(column.top)
bottom.linkTo(column.bottom)
start.linkTo(guideline)
}
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
I think Row layout is enough.
#Preview(showBackground = true, heightDp = 100)
#Composable
fun ListItem(item: PlateUI.Plate = PlateUI.Plate()) {
Card(
shape = RoundedCornerShape(8.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(8.dp),
text = "Code\n${item.code}",
textAlign = TextAlign.Center
)
Box(
Modifier
.fillMaxHeight()
.width(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.weight(1f)
.padding(8.dp),
text = item.name,
textAlign = TextAlign.Center
)
}
}
}