Lets say I have a constraint layout with a column and a button inside of it. Button is constrained to the bottom of the screen.
How can I detect in compose if button covers text column? In other words - how can I check if the text column is fully visible?
Text column can become partially visible when keyboard is opened and windowSoftInputMode is set to adjustResize.
ConstraintLayout(
modifier = Modifier.fillMaxHeight()
) {
val (text, button) = createRefs()
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.constrainAs(text) {
top.linkTo(parent.top)
bottom.linkTo(button.top)
height = Dimension.fillToConstraints
}
) {
Text(
modifier = Modifier
.padding(horizontal = 40.dp)
.fillMaxWidth(),
text = "Hello, this is text"
)
}
Button(
modifier = Modifier
.padding(
top = 20.dp,
bottom = 20.dp,
start = 40.dp,
end = 40.dp
)
.fillMaxWidth()
.constrainAs(button) {
bottom.linkTo(parent.bottom)
},
onClick = {}
) {
Text(
modifier = Modifier
.padding(horizontal = 20.dp)
.fillMaxWidth(),
text = "And this is button"
)
}
}
Related
The dragging part is possible on whole rectangle (Yellow in video) and I want it to be only allowed on the Grey icon
I can drag any part of the yellow part, whether up or down, I want to allow that behavior of dragging only on the Grey part
Left Video is the same as right video, except I made the right's sheetBackgroundColor transparent
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun HomeScreen(modifier: Modifier = Modifier) {
BottomSheetScaffold(
topBar = { AppBar() },
sheetElevation = ZERO_DP,
sheetPeekHeight = BOTTOM_ICON_CONTAINER_SIZE,
sheetBackgroundColor = Color.Transparent,
sheetContent = {
BottomSheetContent(modifier)
}
) {
HomeContent()
}
#Composable
fun BottomSheetContent(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
) {
Box(
modifier = modifier
.padding(end = SPACING_QUADRUPLE)
.align(Alignment.End)
.clip(
RoundedCornerShape(
topStart = TRIPLE_CORNER_DP,
topEnd = TRIPLE_CORNER_DP
)
)
.size(BOTTOM_ICON_CONTAINER_SIZE)
.background(MaterialTheme.colors.secondary)
,
contentAlignment = Alignment.BottomCenter
)
{
Icon(
modifier = modifier,
painter = painterResource(id = R.drawable.ic_qr_code),
contentDescription = stringResource(
id = R.string.bottom_sheet_puller
),
tint = Color.Unspecified
)
}
Text(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(
start = SPACING_DOUBLE,
end = SPACING_DOUBLE,
bottom = SPACING_NORMAL
),
text = "Scan Serial With QR",
style = MaterialTheme.typography.h3,
)
Box(
modifier = modifier
.fillMaxSize()
.background(color = Color.DarkGray)
)
}
}
Wrong Behavior:
Correct Intended Behavior:
I tried replacing a rectangle composables with simple box, but bottom sheet was still considering the full width of the composable
There must be a better solution to intercept a drag gesture and leave all of it within the green box only, but this might suffice.
I made some changes to your BottomSheetContent, intercepting a drag gesture from a Row weighted transparent component and leaving it empty, you can try this one, and the drag gesture is only accepted by the green box,
#Composable
fun BottomSheetContent(
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier
.fillMaxWidth()
.fillMaxHeight(0.8f)
) {
Row {
Box(
modifier = Modifier
.weight(1f)
.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState {
Toast.makeText(context, "Non Draggable Area", Toast.LENGTH_SHORT).show()
}
).fillMaxWidth().height(150.dp).background(Color.Transparent)) {
}
Box(
modifier = modifier
.padding(end = 8.dp)
.clip(
RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp
)
)
.size(150.dp)
.background(MaterialTheme.colors.secondary),
contentAlignment = Alignment.BottomCenter
) {
Icon(
modifier = modifier,
imageVector = Icons.Default.Add,
contentDescription = "",
)
}
}
Text(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colors.surface)
.padding(
start = 8.dp,
end = 8.dp,
bottom = 4.dp
),
text = "Scan Serial With QR",
style = MaterialTheme.typography.h3,
)
Box(
modifier = modifier
.fillMaxSize()
.background(color = Color.DarkGray)
)
}
}
I can't show my pointer click here, but the toast shows when the left area outside of the green box is being dragged.
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'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
}
My Problem:
I want to place my Text() content to center of the page in Scaffold.
I tried "textAlign = TextAlign.Center" - It align the text in horizontal area alone. Not align the text in vertical area.
My code:
#Composable
fun ScaffoldWithTopBar() {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Top App Bar")
},
navigationIcon = {
IconButton(onClick = {}) {
Icon(Icons.Filled.ArrowBack, "backIcon")
}
},
backgroundColor = MaterialTheme.colors.primary,
contentColor = Color.White,
elevation = 10.dp
)
}, content = {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xff8d6e63)),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
}
Text(
text = "Content of the page",
fontSize = 30.sp,
color = Color.White
)
})
}
Note: I haven't place this text into Column. I used directly.
My output:
Question:
How to place the text into the center of the parent?
Assign the fillMaxSize() to the parent container, not to the Text:
Box(Modifier.fillMaxSize(),
contentAlignment = Center
) {
Text(
text = "Content of the page",
fontSize = 30.sp,
modifier = Modifier
.background(Color(0xffb3e5fc)),
)
}
or:
Column(Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Content of the page",
fontSize = 30.sp,
modifier = Modifier
.background(Color(0xffb3e5fc)),
)
}
You need to place it inside a Box, and specify contentAlignment
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.fillMaxSize()
) {
Text(
text = "Content of the page",
fontSize = 30.sp,
modifier = Modifier
.background(Color(0xffb3e5fc)),
)
}
If you wanna align different Box children differently, you can use .align modifier for each item. Note that it's only available inside BoxScope, also there's same modifier for Column and Row.
Box(
modifier = Modifier
.fillMaxSize()
) {
Text(
text = "Content of the page", textAlign = TextAlign.Center,
fontSize = 30.sp,
modifier = Modifier
.background(Color(0xffb3e5fc))
.align(Alignment.Center)
)
}
Box(
modifier = Modifier
.fillMaxSize()
) {
Text(
text = stringResource(id = R.string.social_list_empty),
style = TextStyle(fontSize = 16.sp),
modifier = Modifier
.padding(8.dp)
.align(Alignment.Center),
textAlign = TextAlign.Center
)
}
Reference : For more sample codes of Jetpack Compose, check this
The content in Scaffold is a Composable function in which there is no position info for items by default. In your case, the default position of Text is the top left of its parent view. The solutions putting the Text inside the Box/Colum works, as now Text is put in the center of the Box or Colum, which takes the whole screen. We see Text is in the center of the screen, while the truth is Text is in the center of Box or Colum.
I am trying to show two text messages besides each other in a row but when the size of the first text is big, the second view gets pushed out of the screen as shown below:
The code:
Row(modifier = Modifier.fillMaxWidth()) {
Text(
text = "safasfasdfsasdssdsaasdsadsdsaasdsasdsasdasddassdsssdasdadsasdasdsd",
modifier = Modifier
.padding(top = 12.dp, bottom = 12.dp, end = 12.dp, start = 10.dp)
.background(Color.Gray)
)
Text(
text = "12:00 am",
style = messageTimeTextStyle,
modifier = Modifier
.padding(horizontal = 16.dp),
maxLines = 1
)
}
The output:
You can apply the weight modifier only to the long text.
The .weight modifier sizes the element's width proportional to its weight relative to other weighted sibling elements in the Row. The parent will divide the horizontal space remaining after measuring unweighted child elements and distribute it according to this weight
Something like:
Row() {
Text(text = "safasfasdfsasdssdsaasdsadsdsaasdsasdsasdasddassdsssdasdadsasdasdsd",
Modifier
.padding(top = 12.dp, bottom = 12.dp, end = 12.dp, start = 10.dp)
.background(Color.Gray)
.weight(1f)
)
Text(
text = "12:00 am",
modifier = Modifier
.padding(horizontal = 16.dp),
maxLines = 1
)
}
or
Row() {
Column(Modifier.weight(1f)){
Text(text = "safasfasdfsasdssdsaasdsadsdsa.." , ....)
}
Column() {
Text( text = "12.00 ..", .....)
}
}
If you wrap your text in boxes and give your boxes weight, then you get what you are looking for:
Row(modifier = Modifier.fillMaxWidth()) {
Box(Modifier.weight(2f)) {
Text(
text = "safasfasdfsasdssdsaasdsafasfasdfsasdssdsaasdsafasfasdfsasdssdsaasd",
Modifier
.padding(top = 12.dp, bottom = 12.dp, end = 12.dp, start = 10.dp)
.background(Color.Gray)
)
}
Box(Modifier.weight(1f)) {
Text(
text = "12:00 am",
modifier = Modifier
.padding(horizontal = 16.dp),
maxLines = 1
)
}
}
If you assign weight only to the Text, it will take all the space available and it will leave just enough space that is needed for the Box. I am copying and editing the same code from the reply above by #Code Poet.
Row(modifier = Modifier.fillMaxWidth()) {
Box(Modifier.weight(2f)) {
Text(
text = "safasfasdfsasdssdsaasdsafasfasdfsasdssdsaasdsafasfasdfsasdssdsaasd",
Modifier
.padding(top = 12.dp, bottom = 12.dp, end = 12.dp, start = 10.dp)
.background(Color.Gray)
)
}
Box {
Text(
text = "12:00 am",
maxLines = 1
)
}
}
}
Preview: