How to set component at end of Row in Jetpack Compose? [duplicate] - android

This question already has answers here:
Compose: wrap text in Row layout, instead of pushing siblings out
(2 answers)
Closed 11 months ago.
I want to set RadioButton component at the end of Row in Jetpack Compose. Tried to using Constraint Layout and moved RadioButton outside the Row but then the RadioButton wasn't centered with other components in Row. What should I do?
Here is my code:
ConstraintLayout {
val (row, button) = createRefs()
Row(
modifier = Modifier
.height(56.dp)
.fillMaxWidth()
.constrainAs(row){
start.linkTo(parent.start)
end.linkTo(parent.end)
},
verticalAlignment = Alignment.CenterVertically
) {
Icon(
/* *** */
)
Text(
text = "mail#gmail.com",
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
)
RadioButton(
/* *** */
)
}
}
What is more i want to cut the Text component if the text is too long (not overlay or underlay the Radio Button)

The simplest solution would be to add a Spacer with Modifier.weight(1f) between your text and the radio button. Row and Column distribute remaining available space between components with a weight Modifier according to their weight. Since there is only one, it will receive all of the remaining space, pushing the radio button to the far right.
For example following code would produce your desired behavior:
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically){
Icon(Icons.Default.Add,null)
Text("Some text here")
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
As I said remaining space is distributed according to the weight of each element, so although this is not what you want to achieve, an example how this might look like
Row(modifier = Modifier.height(56.dp).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Add, null)
Spacer(Modifier.weight(1f).fillMaxHeight().background(Color.Red)) // height and background only for demonstration
Text("Some text here")
Spacer(Modifier.weight(4f).fillMaxHeight().background(Color.Green)) // height and background only for demonstration
RadioButton(selected = false, onClick = { /*TODO*/ })
}
will get you
The remaining space after measuring the icon, text and radio button are distributed as 20% to the red Spacer and 80% to the green one, since that is their share of the total weight (1/5 and 4/5)

You could create a Box that fills the rest of the Row and put the Button inside it. You can then align the Button to the right.
Box(modifier = Modifier.fillMaxWidth()) {
RadioButton(modifier = Modifier.align(Alignment.End)){}
}
The same can be achieved with columns instead of the Box, but then every separate element inside the row should have a column wrapping it.

Related

Horizontal Scrolling inside LazyColumn in Jetpack Compose

I'm currently trying to recreate a behavior, upon adding a new element to a LazyColumn the items start shifting to the right, in order to represent a Tree and make the elements easier to read.
The mockup in question:
Documentation
Reading through the documentation of Jetpack Compose in Lists and grids I found the following.
Keep in mind that cases where you’re nesting different direction layouts, for example, a scrollable parent Row and a child LazyColumn, are allowed:
Row(
modifier = Modifier.horizontalScroll(scrollState)
) {
LazyColumn {
// ...
}
}
My implementation
Box(Modifier.padding(start = 10.dp)) {
Row(
modifier = Modifier
.horizontalScroll(scrollState)
.border(border = BorderStroke(1.dp, Color.Black))
) {
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
for (i in 0..25) {
item {
OptionItem(Modifier.padding(start = (i*20).dp))
}
item {
TaskItem(Modifier.padding(start = (i*10).dp))
}
}
}
}
.
.
.
}
OptionItem represents the element with the dot at the beginning, and TaskItem the other one.
When testing the LazyColumn, it appears as if instead of having a fixed size, the size of the column starts growing just after the elements have gone outside the screen, this causes a strange effect.
As you can see in the GIF, the width of the column starts increasing after the elements no longer fit in the screen.
The Question
I want to prevent this effect from happening, so is there any way I could maintain the width of the column to the maximum all the time?
The reason that applying a simple fillMaxWidth will not work because you are telling a composable to stretch to max, but that is impossible because the view itself can stretch indefinitely since it can be horizontally scrollable. I'm not sure why do you want to prevent this behavior but perhaps maybe you want your views to have some initial width then apply the padding, while maintaining the same width. what you can do in such case is simply give your composables a specific width, or what you can do is to get the width of the box and apply them to your composables by width (i used a text in this case)
val localDensity = LocalDensity.current
var lazyRowWidthDp by remember { mutableStateOf(0.dp) }
Box(
Modifier
.padding(start = 10.dp)
.onGloballyPositioned { layoutCoordinates -> // This function will get called once the layout has been positioned
lazyRowWidthDp =
with(localDensity) { layoutCoordinates.size.width.toDp() } // with Density is required to convert to correct Dp
}
) {
val scrollState = rememberScrollState()
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(scrollState)
) {
items(25) { i ->
Text(
text = "Hello",
modifier = Modifier
.padding(start = (i * 20).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
items(25) { i ->
Text(
text = "World",
modifier = Modifier
.padding(start = (i * 10).dp)
.width(lazyRowWidthDp)
.border(1.dp, Color.Green)
)
}
}
}
Edit:
you can apply horizontal scroll to the lazy column itself and it will scroll in both directions

Jetpack Compose : Scrollable column with fractions

I have 3 elements in a column.
I want the 1st element to take 50% of screen and the remaining to take rest of space or scroll if needed.
I have tried the following:
Using verticallScroll() with fractions is not working. If i disable verticallScroll() then the 1st element takes 50% of the screen but the screen does not scrolls if needed.
Column(
modifier = Modifier
.background(color = Color.Black)
.padding(horizontal = 15.dp)
.verticalScroll(state = rememberScrollState(), enabled = true)
.fillMaxSize()
) {
QuickTipsCard(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.5f),
backgroundColor = LightBlue,
title = "Good morning!",
description = "0 out of 10 task today",
titleStyle = Typography.h1
)
}
You can wrap the Column in a BoxWithConstraints component and use the maxHeight property to calculate the desired heights for the elements in the column.

How to apply Materials "Responsive layout grid" to Android Compose?

I would like to align my components based on the Material responsive layout grid, as visualized by from https://material.io/design/layout/responsive-layout-grid.html#columns-gutters-and-margins.
I don't necessarily want my content to be included in a column but rather put baselines on either a start or an end of a column.
A simple example:
// FIXME: What to do?
#Composable
fun example() {
Button(onClick = {
}, text = "Stretch me between start of column 2 and end of column 4"
)
}
My best guess is that I need to use this library somehow: https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary
But I'm still clueless...
How can I achieve what I want to do in Jetpack Compose?
The requirement is not clear. Please comment if this is not what you are looking for.
To have a button with a width of 2 columns in a 4 column grid system. You can use weight and Spacer to achieve the requirement.
This starts at starting of Column 2 and ends at the ending of Column 3.
Row(
modifier = Modifier.fillMaxWidth(),
) {
Spacer(modifier = Modifier.weight(1f))
Button(
onClick = { /*TODO*/ },
modifier = Modifier
.fillMaxWidth()
.weight(2f),
) {
Text(text = "Center Button")
}
Spacer(modifier = Modifier.weight(1f))
}
Using weight and padding modifiers, along with Spacer we can achieve most of the grid requirements.

Fill height for child in Row

I try to achieve this layout but don't really know how:
currently it looks like this:
using this Code:
#Preview(widthDp = 150)
#Composable
fun Item() {
Card(shape = RoundedCornerShape(8.dp)) {
Row {
Box(Modifier.background(Color.Yellow).weight(1f)) {
SomeMoreContentWithUnknownHeight()
}
Box(Modifier.width(20.dp).height(IntrinsicSize.Max).background(Color.Green))
}
}
}
I tried to set the height of the second Box to IntrinsicSize.Max but that didn't change anything. I am currently running Jetpack Compose in Version 1.0.0-beta07
You have to apply Modifier.height(IntrinsicSize.Min) to your Row and .fillMaxHeight() to the 2nd Box.
Something like:
Card(shape = RoundedCornerShape(8.dp)) {
Row(Modifier.height(IntrinsicSize.Min)) {
Box(Modifier
.background(Color.Yellow)
.weight(1f)
) {
//Box(Modifier.height(50.dp))
}
Box(Modifier.width(20.dp)
.fillMaxHeight()
.background(Color.Green)
){
//........
}
}
}
As explained in the doc:
The Row composable's minIntrinsicHeight will be the maximum minIntrinsicHeight of its children. The Green Box element's minIntrinsicHeight is 0 as it doesn't occupy space if no constraints are given; the Yellow Box minIntrinsicHeight will be that of the content given a specific width. Therefore, the Row element's height constraint will be the max minIntrinsicHeight of the Yellow Box content. The Green Box will then expand its height to the height constraint given by the Row.

How do layouts work in Jetpack Compose and how do they relate to XML?

I have some text.
I want to centre it on the screen.
I am using Jetpack Compose.
How do I do this?
I know that there are three types of layouts in Jetpack Compose.
Box
Column
Horizontal
Which one should I use?
I don't know how layouts work.
Are they full screen by default like in XML?
If so, how do I position elements like ConstraintLayout?
How do I set padding and margin from only one side and how do I link elements?
I guess all your questions can be clarified if you follow the Compose Pathway. But I'll try to summarize for you...
You can organize your components using one of the following "layout managers" (which in Compose are just called layouts):
Column (similar to LinearLayout with vertical orientation)
Row (similar to LinearLayout with horizontal orientation)
Box (similar to FrameLayout)
and ConstraintLayout.
If you need something different of these, you can create a custom layout using the Layout composable.
"Which one should I use?"
You can use any of these, depending of the case... To simply display a text in the center of the screen, you can achieve with all of them.
Using Column:
Column(
Modifier.fillMaxSize(), // to fill the whole screen
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Hello")
}
Using Box
Box(
Modifier.fillMaxSize()
) {
Text(text = "Hello",
modifier = Modifier.align(Alignment.Center))
}
"Are they full screen by default like in XML?"
No, they are "wrap_content" by default.
"how do I position elements like ConstraintLayout? How do I set padding and margin from only one side and how do I link elements?"
You need to declare the references to the components and then positioning them accordingly.
Here is a simple example...
ConstraintLayout(modifier = Modifier.fillMaxSize().padding(16.dp)) {
// Creating refs...
val (text1Ref, edit1Ref, btn1Ref, btn2Ref) = createRefs()
Text("Name",
// Linking the reference to this component
modifier = Modifier.constrainAs(text1Ref) {
// linking the top of this component to the parent top
top.linkTo(parent.top)
centerHorizontallyTo(parent)
})
TextField(
value = "",
onValueChange = {},
label = { Text("Name") },
modifier = Modifier.padding(top = 8.dp)
.constrainAs(edit1Ref) {
start.linkTo(parent.start)
end.linkTo(parent.end)
// linking this component with the previous component
top.linkTo(text1Ref.bottom)
})
Button(onClick = {},
content = { Text("OK") },
modifier = Modifier.padding(top = 8.dp).constrainAs(btn1Ref) {
end.linkTo(edit1Ref.end)
top.linkTo(edit1Ref.bottom)
}
)
TextButton(onClick = {},
content = { Text("Cancel") },
modifier = Modifier.padding(end = 8.dp)
.constrainAs(btn2Ref) {
end.linkTo(btn1Ref.start)
baseline.linkTo(btn1Ref.baseline)
}
)
}

Categories

Resources