Wrap content to a text with clipped shape in Compose? - android

I have a text compose and I want to apply a triangle shape on it, I applied the tringle successfully but the text doesn't wrap inside the triangle shape, it seems that the shape doesn't cut the view to be tringle, it only draws above it or thing like that.
here is the code:
Text(
modifier = Modifier
.clip(TriangleShape)
.background(
Brush.horizontalGradient(
listOf(
Color(0xff4F0D37),
Color(0xffD73046)
)
)
),
text = "150",
color = Color.White,
)
Any tip will be appreciated :)

Here's how I did it:
Text(
modifier = Modifier
.clip(TriangleShape)
.background(
Brush.horizontalGradient(
listOf(
Color(0xff4F0D37),
Color(0xffD73046)
)
)
)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(placeable.width * 2, placeable.height * 2) {
placeable.placeRelative(0, 0)
}
},
text = "150",
color = Color.White,
)
I used the layout lambda expression to double the width and the height of the composable, while keeping the everything else the same. Took some messing around with rows and columns but I think this is a fitting solution.
Here's the result:

BoxWithConstraints{
val constraints = size
Text(
modifier = Modifier
.clip(TriangleShape)
.background(
Brush.horizontalGradient(
listOf(
Color(0xff4F0D37),
Color(0xffD73046)
)
)
),
text = "150",
color = Color.White,
fontSize = (constraints.width / 2) // ... Modify if necessary
)
}
Here I am using the size parameter exposed by BoxWithConstraints. This is the size of the box, which I am storing in a variable to later re-use to set the font size of the text accordingly. Text positioning can be done easily. Use Modifier.align(TopStart)

Related

How to set Box(contentAlignment=CenterVertically40%DownThePage)? Need advanced alignment option for Box

I'm building this screeen from the Android Jetpack Compose lessons, and I want to somewhat-center-vertically the middle / 'Full Name' red box -- but I don't want it fully centered (i.e. 50% down the page), I want it 40% down the page, but the built-in 'contentAlignment' modifiers only cover 'Top', 'Center', and 'Bottom' -- I want something in between.
This is my current code, and it looks fine-ish, but I want to know how to manipulate the vertical alignment of the contents of the Box more fine-grained.
#Composable
fun Profile(){
Box(
modifier = Modifier
.padding(16.dp)
.fillMaxSize(),
contentAlignment = Alignment.Center
,
) {
Column() {
// icon
Text("aslkjdf",
fontSize = 48.sp)
// Full Name
Text("aslkjdf",
fontSize = 48.sp)
// Title
Text("aslkjdf",
fontSize = 48.sp)
}
}
}
You can use the BiasAlignment.
A bias of -1 represents alignment to the start/top, a bias of 0 will represent centering, and a bias of 1 will represent end/bottom.
Just note that the Alignment.Center is defined as:
val Center: Alignment = BiasAlignment(0f, 0f)
You can use something like:
Box(
modifier = Modifier
.padding(16.dp)
.fillMaxSize(),
contentAlignment = BiasAlignment(
horizontalBias = 0f,
verticalBias = -0.2f
)
) {
Column(
modifier = Modifier.fillMaxWidth().border(1.dp,Red),
horizontalAlignment = Alignment.CenterHorizontally
){
// icon
Icon(Icons.Default.Email,"", modifier = Modifier.size(64.dp))
// Full Name
Text("FullName",
fontSize = 36.sp)
// Title
Text("Title",
fontSize = 24.sp)
}
}
Inside the -1, 1 range, the obtained alignment will position the aligned size fully inside the available space, using as center
val y = centerY * (1 + verticalBias)

Jetpack Compose TextField cuts text on scroll

I have a TextField with a fixed height. When the user enters a longer text it will scroll. It will cut off any text within the padding when scrolling:
Basically something like this:
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { value -> text = value },
modifier = modifier
.fillMaxWidth()
.height(100.dp),
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
backgroundColor = Color.Transparent
)
)
It is possible to adjust/remove the padding for a TextField, by using BasicTextField directly, e.g. see this stack overflow question.
However I want to keep the padding, but without the clipping of the text when the user scrolls. A simple Text Composable has this behavior.
You can use BasicTextField and modify its decorationBox parameter.
Simply put innerTextField() inside a scrollable Column and add Spacers at the top and the bottom of it.
var text by remember {
mutableStateOf("Hello Stackoverflow")
}
val paddingSize = remember { 16.dp }
BasicTextField(
modifier = Modifier
.height(100.dp),
value = text,
onValueChange = { text = it },
decorationBox = { innerTextField ->
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(state = rememberScrollState())
) {
Spacer(modifier = Modifier.height(paddingSize))
innerTextField()
Spacer(modifier = Modifier.height(paddingSize))
}
}
)
Just use the maxLines parameter of the TextField to avoid clipping. Set it to 1 or 2, per your case, then adjust the height/font-size so the max lines you specify are accomodated correctly. It will likely start to snap to the visible portion, cutting the extra stuff entirely.

How to remove padding in text line on Jetpack Compose

I faced with a problem related to Text element in Jetpack Compose. I wanted to place two separated Text elements under each other but I can't remove their inner paddings to make them look like not so separated.
As you can see on pic below Text with "02" has these paddings. Is there any way to crop them?
I tried to use .offset() and .height() modifiers to change the Text box size, but it looks like a really awful solution.
Code example to reproduce:
Box(
modifier = Modifier
.height(65.dp)
.width(65.dp)
.background(Color.White)
) {
Column(
modifier = Modifier
.align(Alignment.Center)
) {
Text(
text = "00",
maxLines = 1,
overflow = TextOverflow.Clip,
style = MaterialTheme.typography.h5.copy(
fontWeight = FontWeight.SemiBold
),
color = Color.Black,
modifier = Modifier
.align(Alignment.CenterHorizontally)
)
Text(
text = "Text",
style = MaterialTheme.typography.body2,
color = Color.Gray,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
}
Thanks in advance!

Setting LazyColumn Item Height to Maximum Available in Jetpack Compose [duplicate]

I have a compose card and inside a row with circle views, I need to make it so the last item in this row is half shown only. Is there a easy way to achieve this without measuring the screen width after everything is drawn and then modify the padding for the row items dynamically to achieve it?)
If you need to calculate the number of elements depending on the size of the cell contents, it is impossible to do this without real measurements.
But if you know exactly how many elements you need to display, you can use Modifier.fillParentMaxWidth with the desired fraction. This is only available in lazy views like LazyRow.
Things are a bit more complicated with spacings: usually contentPadding is used to offset the first element, which reduces the parent size, depending on which Modifier.fillParentPaxMaxWidth calculates the actual value. Also, if you apply it to both start and end, you won't get the desired result. That's why I apply it only to start, and create an equivalent effect at the end with another item.
I also manually surround the Spacers item instead of using Arrangement.spacedBy, because the spacers should be inside the Modifier.fillParentMaxWidth too.
val spacing = 20.dp
val halfSpacing = spacing / 2
val shape = RoundedCornerShape(20)
LazyRow(
contentPadding = PaddingValues(start = halfSpacing),
modifier = Modifier
.padding(30.dp)
.border(1.dp, color = Color.Black, shape = shape)
.clip(shape)
.padding(vertical = 20.dp)
) {
items(10) {
Row(
Modifier
.fillParentMaxWidth(1f / 3.5f)
) {
Spacer(Modifier.size(halfSpacing))
Box(
Modifier
.weight(1f)
.aspectRatio(1f)
.background(Color.Blue, shape = CircleShape)
)
Spacer(Modifier.size(halfSpacing))
}
}
item {
Spacer(Modifier.size(halfSpacing))
}
}
Result:
LazyColumn variant:
val spacing = 20.dp
val halfSpacing = spacing / 2
val shape = RoundedCornerShape(20)
LazyColumn(
contentPadding = PaddingValues(top = halfSpacing),
modifier = Modifier
.padding(30.dp)
.border(1.dp, color = Color.Black, shape = shape)
.clip(shape)
.padding(horizontal = 20.dp)
) {
items(10) {
Column(
Modifier
.fillParentMaxHeight(1f / 3.5f)
) {
Spacer(Modifier.size(halfSpacing))
Box(
Modifier
.weight(1f)
.aspectRatio(1f)
.background(Color.Blue, shape = CircleShape)
)
Spacer(Modifier.size(halfSpacing))
}
}
item {
Spacer(Modifier.size(halfSpacing))
}
}

Compose: Create Text with Circle Background

Coming from SwiftUI, I wanted to create a view of a Text where it has a background of a Circle, where the circle's width/height grow as the text inside Text gets longer.
Since there's no Circle() in Compose like there is in SwifUI, I created just a Spacer instead and clipped it. The code below is using ConstraintLayout because I don't know how I would get the width of the Text in order to set the size of my Circle composable to be the same:
#Composable
fun CircleDemo() {
ConstraintLayout {
val (circle, text) = createRefs()
Spacer(
modifier = Modifier
.background(Color.Black)
.constrainAs(circle) {
centerTo(text)
}
)
Text(
text = "Hello",
color = Color.White,
modifier = Modifier
.constrainAs(text) {
centerTo(parent)
}
)
}
}
I can use a mutableStateOf { 0 } where I update that in an onGloballyPositioned modifier attached to the Text and then set that as the requiredSize for the Spacer, but 1. that seems stupid and 2. the Spacer now draws outside the boundaries of the ConstraintLayout.
Visually I want to achieve this:
How would I go about doing this? Thank you :)
It is also possible to use drawBehind from the modifier of the textView itself such as below:
Text(
modifier = Modifier
.padding(16.dp)
.drawBehind {
drawCircle(
color = red,
radius = this.size.maxDimension
)
},
text = "Hello",
)
of course, you can further customize the radius and other properties as you wish!
You have to calculate the dimension of the background circle depending on the dimension of the text.
You can use a custom modifier based on Modifier.layout:
fun Modifier.circleLayout() =
layout { measurable, constraints ->
// Measure the composable
val placeable = measurable.measure(constraints)
//get the current max dimension to assign width=height
val currentHeight = placeable.height
val currentWidth = placeable.width
val newDiameter = maxOf(currentHeight, currentWidth)
//assign the dimension and the center position
layout(newDiameter, newDiameter) {
// Where the composable gets placed
placeable.placeRelative((newDiameter-currentWidth)/2, (newDiameter-currentHeight)/2)
}
}
Then just just apply it the Text with a background with a CircleShape:
Text(
text = "Hello World",
textAlign = TextAlign.Center,
color = Color.White,
modifier = Modifier
.background(Color.Black, shape = CircleShape)
.circleLayout()
.padding(8.dp)
)
#Composable
fun Avatar(color: Color) {
Box(
modifier = Modifier
.size(size.Dp)
.clip(CircleShape)
.background(color = color),
contentAlignment = Alignment.Center
) {
Text(text = "Hello World")
}
}
Use a background drawable of a black circle inside a transparent color. The background drawable will stretch to fill the view, and circles should stretch well without artifacting.

Categories

Resources