How to add left border in a Row/Column/Card Compose - android

I'm trying to add a left/start vertical border to view (Column), Not able to get the solution. as of now was trying to achieve using a divider inside the column it also need a height, but it depends on the contents inside the column, sometime it may grow.
Column(modifier = Modifier.padding(start = 34.dp)) {
Divider(
color = Color.Red,
modifier = Modifier
.height(100.dp)
.padding(end = 34.dp).width(2.dp)
)

You can use the drawWithCache modifier using the drawLine function.
Something like:
Column(modifier =
Modifier
.padding(start = 34.dp)
.size(100.dp, 75.dp)
.drawWithCache {
onDrawWithContent {
// draw behind the content the vertical line on the left
drawLine(
color = Color.Red,
start = Offset.Zero,
end = Offset(0f, this.size.height),
strokeWidth= 1f
)
// draw the content
drawContent()
}
}
){
//...content
}
If you want to use a Divider you can use fillMaxHeight() applying an intrinsic measurements to its parent container.
Something like:
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
Divider(
color = Color.Red,
modifier = Modifier
.fillMaxHeight() //important
.width(2.dp)
)
Box(Modifier.fillMaxWidth().height(100.dp).background(Yellow))
}

You can achive this with Modifier.drawBehind and drawLine
Code
TextButton(
onClick = {
//Click Functions
},
modifier = Modifier.drawBehind {
val strokeWidth = 1 * density
//Draw line function for left border
drawLine(
Color.LightGray,
Offset(0f, strokeWidth),
Offset(0f, size.height),
strokeWidth
)
}
)
{
Text("Left Border")
}
Output

Related

Why the compose canvas draw things only show at preview function but not display on the running screen

Descript
I want to draw a line at the compose with the compose canvas inside a compose widget,but it's just show at the preview!
If just copy the canvas part code,it can Correct display!
The compose code
Card(
elevation = 6.dp,
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = backgroundColor),
) {
AsyncImage(modifier = Modifier
.height(180.dp)
.width(120.dp)
.padding(12.dp)
... )
Row(
modifier = Modifier.fillMaxSize()
) {
Column(
modifier = Modifier
.fillMaxHeight()
.weight(0.7f)
) {
...
}
Canvas(
modifier = Modifier
.weight(0.3f)
.fillMaxHeight()
.padding(start = 6.dp)
) {
// NOT SHOW !!!
drawLine(
color = Color.White,
start = Offset(0f, 0f),
end = Offset(0f, size.height),
strokeWidth = 2f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f),
)
}
}
}
}
SceenShot
It might be an issue related to size of Canvas. Check if it doesn't return width or height zero in your Composable.
In Jetpack Compose Canvas is nothing other than Spacer with Modifier.drawBehind{}
#Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw))
Which you can use Modifier.drawWithContent{} on your own Composable instead using Box and Canvas Composabble
you can use Modifier.drawWithContent for instance such as
Column {
val drawModifier = Modifier
.drawWithContent {
drawContent()
drawLine(
color = Color.White,
start = Offset(size.width*.8f, 0f),
end = Offset(size.width*.8f, size.height),
strokeWidth = 2f,
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 20f), 0f),
)
}
.width(200.dp)
.height(100.dp)
Box(modifier = drawModifier.background(Color.Red)) {
Text("Hello World")
}
}

Border radius is not changing based on shape when user click on it jetpack compose

Hey guys I am using RoundedCornerShape(4.dp) to my Surface which looks fine. When I tried to click on the item it not showing me 4dp corner in Surface. I tried this stack overflow 1 and stack overflow 2 but nothing works.
binding.itemComposable.setContent {
Column(modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(12.dp)) {
val options = getOptions()
options.forEachIndexed { _, optionText ->
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val backgroundColor = if (isPressed) DuckEggBlue else OffWhite
val textColor = if (isPressed) TealBlue else Slate
val borderWidth = if (isPressed) 1.dp else 0.dp
val borderColor = if (isPressed) Aqua else OffWhite
val clickable = Modifier.clickable(
interactionSource = interactionSource,
indication = rememberRipple(true)
) {
println("Item Click")
}
Surface(
modifier = Modifier
.then(clickable)
.border(borderWidth, borderColor),
shape = RoundedCornerShape(4.dp)
) {
Text(
modifier = Modifier
.fillMaxWidth()
.background(backgroundColor)
.padding(16.dp),
text = optionText,
style = Typography.h3,
fontWeight = FontWeight.Medium,
color = textColor
)
}
}
}
}
Without click on item corner is 4 dp
When I click it's not changing corner
If you want to handle the click on a Surface you have to use the function that accepts an onClick():
Surface(
onClick = {},
shape = RoundedCornerShape(4.dp),
border = BorderStroke(borderWidth,borderColor),
interactionSource = interactionSource
)
Create a variable for shape
val shape = RoundedCornerShape(4.dp)
Use it in Modifier.clip() and Modifier.border() like this,
Surface(
modifier = Modifier
.clip(shape)
.border(
width = borderWidth,
color = borderColor,
shape = shape,
)
.then(clickable),
// shape = shape,
)
shape in border() specifies the shape of the border which by default is RectangleShape. Hence, you are seeing the rectangle border.
shape in clip() changes the shape of the composable before the click action is added. This is to make the ripple effect appear only on the given shape.
Note: Order of modifiers are important.
The shape in the Surface may not be needed after these changes.
If youre using Surface to wrapping the content, try to add a container inside the content for example Box or Column. Then use your Surface only as a shape mask, the background and other content will be flexible as you want.
This is the example
Surface(
modifier = Modifier
.then(clickable)
.border(borderWidth, borderColor),
shape = RoundedCornerShape(4.dp)
) {
Box(modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.background(Color.Green)){
Text(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
text = optionText,
style = Typography.h3,
fontWeight = FontWeight.Medium,
color = textColor
)
}
}

Jetpack Compose semicircle using Canvas

I am trying to create a semicircle speed progress bar in Jetpack Compose. Unless the view is square the semicircle will not look as expected, if I use 1:2 width: height it will be flattened. I want a Composable representing half of the circle where I don't have unusable bottom half of the view.
Box(
modifier = modifier
.background(Color.Red)
) {
Canvas(modifier = Modifier.size(300.dp)) {
drawArc(
color = Color.LightGray,
-180f,
180f,
useCenter = false,
style = Stroke(8.dp.toPx(), cap = StrokeCap.Round)
)
}
Text(
modifier = Modifier.align(alignment = Alignment.Center),
text = "20 Mbps",
color = Color.White,
fontSize = 20.sp
)
}
The expected outcome would be a reusable semicircle composable with a height of the actual semicircle so I can easily position other content against it. The expected view size is marked by a dotted green line.
As i mentioned in comments arc uses rectangle if you want a semi arc that covers whole hight just double the height you draw arc with
#Composable
private fun ArcComposable(modifier: Modifier) {
Box(
modifier = modifier
.background(Color.Red)
) {
Canvas(modifier = Modifier
.size(300.dp)
.clipToBounds()) {
drawArc(
color = Color.LightGray,
-180f,
180f,
useCenter = false,
size = Size(size.width, size.height * 2),
style = Stroke(8.dp.toPx(), cap = StrokeCap.Round)
)
}
Text(
modifier = Modifier.align(alignment = Alignment.Center),
text = "20 Mbps",
color = Color.White,
fontSize = 20.sp
)
}
}
I added Modifier.clipToBounds() because of strokeCap round which is added to length of the line by default. You can just reduce size and height few px to match inside the canvas. Canvas by default even if you don't set a modifier with size it draws anything out of its bounds unless you use Modifier.clipToBounds()
private fun ArcComposable(modifier: Modifier) {
Box(
modifier = modifier
.background(Color.Red)
) {
Canvas(
modifier = Modifier
.size(300.dp)
// .clipToBounds()
) {
drawArc(
color = Color.LightGray,
-180f,
180f,
useCenter = false,
topLeft = Offset(4.dp.toPx(), 6.dp.toPx()),
size = Size(size.width - 8.dp.toPx(), size.height * 2 - 20.dp.toPx()),
style = Stroke(8.dp.toPx(), cap = StrokeCap.Round)
)
}
Text(
modifier = Modifier.align(alignment = Alignment.Center),
text = "20 Mbps",
color = Color.White,
fontSize = 20.sp
)
}
}

Android - Jetpack Compose draw text with centered lines on sides

I am trying to make this simple view in Compose, but cant seem to get it right.
I have tried using dividers, and now switched to canvas, but cant seem to get correct result.
Row{
Line()
ClickableText(text = AnnotatedString("Show replies"),
modifier = Modifier.weight(1f),
onClick = { showReplies.value = true })
Line()
}
#Composable
fun RowScope.Line() {
Canvas(modifier = Modifier.fillMaxSize().weight(1f)) {
// Fetching width and height for
// setting start x and end y
val canvasWidth = size.width
val canvasHeight = size.height
// drawing a line between start(x,y) and end(x,y)
drawLine(
start = Offset(x = 0f, y = canvasHeight/2),
end = Offset(x = canvasWidth, y = canvasHeight/2),
color = Color.Red,
strokeWidth = 5F
)
}
}
I have played with arragments, weights, sizes, but always get some quirky result.
Try this:
#Composable
fun Replies() {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier
.height(2.dp)
.weight(1f)
.background(Color.Gray)) {}
ClickableText(
text = AnnotatedString("Show replies"), onClick = {}, modifier = Modifier.weight(1f),
style = TextStyle(
textAlign = TextAlign.Center
),
)
Box(modifier = Modifier
.height(2.dp)
.weight(1f)
.background(Color.Gray)) {}
}
}

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