Creating arc inside arch with text using jetpack compose - android

I want to achieve like below, I have tried using the draw arc of compose and able to create one arc but not able to achieve another arch inside it with text at the ends to the arc.
Canvas(
modifier = modifier
) {
drawArc(
color = backgroundColor,
180f,
180f,
false,
style = Stroke(strokeWidth.toPx()),
size = Size(size.width, size.height),
)
drawArc(
color = fillColor,
180f,
percentage * 180f,
false,
style = Stroke(strokeWidth.toPx()),
size = Size(size.width, size.height),
)

Related

compose modifiy icon set part of the color

I have an icon to indicate sequence.
My requirement is that when the order is reversed, the lower half of the icon turns blue; when the order is turned, the upper half of the icon turns blue.
I found a related question, but it conflicts with my needs in two points. First, I don't know how to write such code in compose. Second, I prefer to use code to control the color transformation.
Using BlendModes you can manipulate any pixel using another shape, png file, drawing or Path. You can refer these answers for more details
Jetpack Compose Applying PorterDuffMode to Image
How to clip or cut a Composable?
Result
Implementation
#Preview
#Composable
private fun Test() {
Image(
modifier = Modifier
.size(100.dp)
.drawWithContent {
val height = size.height
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
// Destination
drawContent()
// Source
drawRect(
Color.Red,
topLeft = Offset(0f, height / 2),
size = Size(size.width, size.height / 2),
blendMode = BlendMode.SrcIn
)
}
},
painter = painterResource(id = R.drawable.arrows),
contentDescription = null
)
}

Android: compose drawArc would not get centered within `Box`

I am trying to show progress using drawArc for compose. I have tried drawBehind modifier to draw a background circle and not trying to draw another circle on top of it to show the progress. The problems is, no matter what I try, the arc turns out to be drawn at top left corner all the time. If I explicitly define the topLeft value, it only works for 1 screen size. So I am trying to get a dynamic size for both circles and also the strokewidth. So for Tablets only the circles should be increased depending on the screensize and also the thickness should be increased for the same. And for the smaller size devices, the value should be decreasing. Here is my code and output:
Column(
modifier = Modifier
.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
BoxWithConstraints(
Modifier
.fillMaxSize()
.background(Color.Yellow),
) {
Canvas(modifier = Modifier
.size(maxWidth, maxHeight)
.background(color = Color.Red)) {
drawCircle(
color = Color.Gray,
radius = (maxWidth /4).toPx(),
style = Stroke(width = 14f, cap = StrokeCap.Round),
)
val sweepAngle = progress/100 * 360
drawArc(
size = Size((maxWidth/2).toPx(),(maxWidth/2).toPx()),
color = Color.Green,
startAngle = -90f,
sweepAngle = sweepAngle,
useCenter = false,
style = Stroke(10f, cap = StrokeCap.Round),
)
}
}
}
PS: I cannot use another circle object since I need to have the tip of the circle round aka cap should be Stroke.Round.
I ended up trying BoxWithConstraints so I can have access to maxWidth and maxHeight
You have to calculate the topLeft offset of the arc as center of the circle - radius of the circle.
Then the size of arc is maxWidth/2 instead of maxWidth/4 = radius.
Something like:
val stroke = 5.dp
drawCircle(
color = Color.Gray,
radius = (maxWidth /4).toPx(),
style = Stroke(width = stroke.toPx(), cap = StrokeCap.Round),
)
val sweepAngle = progress/100 * 360
val offsetx = this.center.x - (maxWidth/4).toPx()
val offsety = this.center.y - (maxWidth/4).toPx()
drawArc(
size = Size((maxWidth/2).toPx(),(maxWidth/2).toPx()),
color = Color.Green,
startAngle = 0f,
sweepAngle = -sweepAngle,
topLeft = Offset(offsetx,offsety),
useCenter = false,
style = Stroke(stroke.toPx(), cap = StrokeCap.Round),
)
}

Jetpack compose canvas clip to rounded arc shape

I have the following two overlaying 360 degrees arc (essentially two circles) clipped by an arc shape. Unlike drawArc, addArc does not take into style = ... which can be used to define cap = StrokeCap.Round. The current clip looks as expected, an arc clipped with hard edges. Is there a way to round out the corners??
I tried using blendMode, but that does not work as expected since the color of the layers could be diffrent opacity, which blend mode takes in consideration when blending.
Canvas(modifier = Modifier.fillMaxSize()) {
val path = Path().apply {
this.addArc(
oval = Rect(
topLeft = Offset(0F, 0F),
bottomRight = Offset(size.width, size.height)
),
startAngleDegrees = 0F,
sweepAngleDegrees = 240F
)
}
clipPath(path = path) {
// following two drawArc could be replaced w/ drawCircle
drawArc(
color = Color.Magenta.copy(alpha = 0.2F),
startAngle = 0f,
sweepAngle = 360f,
useCenter = false,
style = Stroke(strokeWidthPx2, cap = StrokeCap.Round),
size = Size(size2, size2),
topLeft = Offset(offset2, offset2)
)
drawArc(
color = Color.Cyan,
startAngle = 0f,
sweepAngle = 360f,
useCenter = false,
style = Stroke(strokeWidthPx, cap = StrokeCap.Round),
size = Size(size1, size1),
topLeft = Offset(offset, offset)
)
}
}

How can I draw a shadow for a path in canvas in jetpack compose

I am drawing a custom shape for a topbar in jetpack compose. I want to draw a shadow for the path.
val topBarShapePath = Path().apply {
moveTo(dpToPixels(leftPadding), 0f)
lineTo(dpToPixels(leftPadding), dpToPixels(dpValue = 110.dp))
arcTo(
Rect(
dpToPixels(leftPadding),
dpToPixels(dpValue = 110.dp),
dpToPixels(dpValue = 32.dp),
dpToPixels(dpValue = 135.dp)
), -180f, -90f, true)
lineTo(
dpToPixels(dpValue = triangleStartX),
dpToPixels(dpValue = rectHeight))
lineTo(
dpToPixels(dpValue = screenWidth),
dpToPixels(dpValue = triangleEndY)
)
lineTo(dpToPixels(dpValue = screenWidth), 0f)
lineTo(dpToPixels(dpValue = leftPadding), 0f)
}
Column(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.drawBehind {
val finalWidth = 40.dp.toPx()
drawPath(
topBarShapePath,
color = topbarcolor)
drawOutline(
outline = Outline.Generic(
topBarShapePath),
brush = Brush.horizontalGradient(),
style = Stroke(
width = 1.dp.toPx(),
)
)
}
)
This is the code I am using to draw the shape, the "drawOutline" was to try and draw a shadow for the path, but I can't figure out how to blur the line.
Any help appreciated.
Here is a screenshot of the result I am looking for:
It's impossible to draw shadow in Canvas at the moment, but you can do it with Modifier.shadow, specifying the needed custom shape, like this:
class TopBarShape(/*some parameters*/): Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density,
) = Outline.Generic(Path().apply {
// your path code
})
}
Modifier.shadow(elevation = 10.dp, shape = TopBarShape(/*your parameters*/))
Sadly this modifier doesn't allow much modifications, it's one of the most starred Compose issues, so hopefully it'll change in future, but as it's not in the latest 1.1-beta I wouldn't expect it at least until 1.2.
If you still think that drawing shadow manually is a needed feature, you can create a feature request.

Arc/Semicircle Shape in Jetpack Compose

I'm looking to draw an arc type shape in Jetpack Compose but I'm really struggling to figure out the best way to do it. When I use drawArc, I can't seem to start the arc from the bottom left corner, it just starts from left middle. I've also tried drawRoundRect but I can't find a way to just round the top left and top right corners.
I'll attach a picture, I don't need the white handle within the shape. Desired result:
drawRoundRect attempt:
drawArc attempt:
You could make it by using a a canvas with your device width but try to increase arc limits beyond device width. With this code
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
val formWidth = (canvasWidth * 2)
val xPos = canvasWidth / 2
drawArc(
Color.Black,
0f,
-180f,
useCenter = true,
size = Size(formWidth, 600f),
topLeft = Offset(x = -xPos, y = canvasHeight - 300)
)
}
Arc at bottom looks like this:
more info
You can draw it like this
Placed it to center for demonstration and border is to show that it doesn't overflow Canvas, if you don't pay attention, Canvas draws out of its borders unless use Modifier.clipbounds() is used on Canvas Modifier.
Trick for drawing half arc is to set two times of Canvas height, since arc is drawn inside a rectangle in principle. Changind height of Modifier of canvas changes height of arc.
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Canvas(
modifier = Modifier
.border(2.dp, Color.Red)
.fillMaxWidth()
.height(80.dp),
) {
val canvasWidth = size.width
val canvasHeight = size.height
drawArc(
Color.Blue,
startAngle = 180f,
sweepAngle = 180f,
useCenter = true,
size = Size(canvasWidth, 2 * canvasHeight)
)
val handleWidth = 200f
val handleHeight = 30f
drawRoundRect(
Color.White,
size = Size(handleWidth, handleHeight),
cornerRadius = CornerRadius(5.dp.toPx(), 5.dp.toPx()),
topLeft = Offset((canvasWidth - handleWidth) / 2, (canvasHeight - handleHeight)/2)
)
}
}
Result
You can check this tutorial about Jetpack Compose which covers Canvas operations in detail.

Categories

Resources