0

if anyone can tell me how to draw this shape

with an inside text, id greatly appreciate it. Is there a way to do it in regular xml or any android api

sdds
  • 15
  • 3

2 Answers2

2

You can use the Canvas to draw any shapes. Below is a sample implementation as per your required shape which you can customize.

Box {
    Canvas(
        modifier = Modifier
            .size(200.dp)
            .padding(40.dp)
    ) {
        val trianglePath = Path().let {
            it.moveTo(this.size.width * .40f, 0f)
            it.lineTo(this.size.width * .50f, -30f)
            it.lineTo(this.size.width * .60f, 0f)
            it.close()
            it
        }
        drawRoundRect(
            Color.LightGray,
            size = Size(this.size.width, this.size.height * 0.95f),
            cornerRadius = CornerRadius(60f)
        )
        drawPath(
            path = trianglePath,
            Color.LightGray,
        )
    }
}

Sample using Canvas

Bullionist
  • 2,070
  • 3
  • 25
  • 42
  • This is answer is pretty good, and upvoted but i have some suggestions to make it better. First, don't create a Path on each draw, remember it and fill it if it's empty. Second, drawing with minus offsets draws out of your Composable, you compansate this with padding but this might not be the case when you want this bubble stick to some other Composable. You will also need to use Modifier.offset this way. Also instead of drawing this to Canvas use GenericShape which also accepts `Path` operations, you will be able to add shadow and border that matches shape and will look exactly as OP asked. – Thracian Dec 02 '22 at 05:44
  • However this also requires use of Modifier.layout{} as in my library to create unused space that should only be reserved for nip/arrow but not content – Thracian Dec 02 '22 at 05:44
0

Easiest way you can do it with shadow and border properties is to create a custom Shape.

enter image description here

If you draw to Canvas alone you will also need to draw shadow and border properties, and need to create offsets inside your Composables bounds.

With GenericShape you can create a Path with roundedRect and add a triangle to top with

fun getBubbleShape(
    density: Density,
    cornerRadius: Dp,
    arrowWidth: Dp,
    arrowHeight: Dp,
    arrowOffset: Dp
): GenericShape {

    val cornerRadiusPx: Float
    val arrowWidthPx: Float
    val arrowHeightPx: Float
    val arrowOffsetPx: Float

    with(density) {
        cornerRadiusPx = cornerRadius.toPx()
        arrowWidthPx = arrowWidth.toPx()
        arrowHeightPx = arrowHeight.toPx()
        arrowOffsetPx = arrowOffset.toPx()
    }

    return GenericShape { size: Size, layoutDirection: LayoutDirection ->

        this.addRoundRect(
            RoundRect(
                rect = Rect(
                    offset = Offset(0f, arrowHeightPx),
                    size = Size(size.width, size.height - arrowHeightPx)
                ),
                cornerRadius = CornerRadius(cornerRadiusPx, cornerRadiusPx)
            )
        )

        moveTo(arrowOffsetPx, arrowHeightPx)
        lineTo(arrowOffsetPx + arrowWidthPx / 2, 0f)
        lineTo(arrowOffsetPx + arrowWidthPx, arrowHeightPx)

    }
}

If you need to have borders instead of addRoundRect you will need arcs and lines to draw for connecting arrow and lines you can refer this answer for drawing rounded rectangle using arcs and lines. If you set border with current setup you will understand what i mean

Then use it as

@Composable
private fun BubbleShapeSample() {
    val density = LocalDensity.current
    val arrowHeight = 16.dp

    val bubbleShape = remember {
        getBubbleShape(
            density = density,
            cornerRadius = 12.dp,
            arrowWidth = 20.dp,
            arrowHeight = arrowHeight,
            arrowOffset = 30.dp
        )
    }

    Column(
        modifier = Modifier
            .shadow(5.dp, bubbleShape)
            .background(Color.White)
            .padding(8.dp)
    ) {


        Spacer(modifier = Modifier.height(arrowHeight))

        Row(modifier = Modifier.padding(12.dp)) {

            Icon(
                modifier = Modifier.size(60.dp),
                imageVector = Icons.Default.NotificationsActive,
                contentDescription = "",
                tint = Color(0xffFFC107)
            )

            Spacer(modifier = Modifier.width(20.dp))
            Text(
                "Get updates\n" +
                        "on questions\n" +
                        "and answers",

                fontSize = 20.sp
            )

            Spacer(modifier = Modifier.width(20.dp))

            Icon(
                imageVector = Icons.Default.Close,
                contentDescription = ""
            )

        }

    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222