ITmob-Ly
发布于 2023-08-28 / 133 阅读
0

在 Jetpack Compose 中怎样实现印章样式(弧形)的文字效果?

项目需求中有时可能需要显示弯曲弧形的文本,比如文字要显示为印章样式,则需要将文字沿弧形路径显示,本文介绍 Android 中如何使用 Jetpack Compose 实现沿着弧形路径排版类似印章的文字效果。

印章样式的文字效果

如下图的效果:

Stamp style text in Jetpack Compose

代码:

@Composable
fun SealStyleCurvedText() {
    val textMeasurer = rememberTextMeasurer()
    val icLauncher = getAppIconBitmap(LocalContext.current)?.asImageBitmap()
    Canvas(
        Modifier
            .padding(16.dp)
            .size(300.dp),
    ) {
        // 绘制红色圆形
        drawCircle(
            color = Color.Red,
            radius = 150.dp.toPx(),
            center = Offset(150.dp.toPx(), 150.dp.toPx()),
            style = Stroke(4.dp.toPx()),
        )

        // 绘制弧形布局的文字,并在上方居中
        drawIntoCanvas {
            val path = android.graphics.Path().apply {
                addArc(
                    RectF(36.dp.toPx(), 36.dp.toPx(), 264.dp.toPx(), 264.dp.toPx()),
                    90F,
                    359.9F,
                )
            }
            it.nativeCanvas.drawTextOnPath(
                "Hello itmob.cn! Hello Jetpack Compose!",
                path,
                0f,
                0f,
                android.graphics.Paint().apply {
                    textSize = 24.sp.toPx()
                    textAlign = android.graphics.Paint.Align.CENTER
                },
            )
        }

        // 绘制底部文字
        drawText(
            textMeasurer = textMeasurer,
            text = buildAnnotatedString {
                withStyle(
                    style = ParagraphStyle(textAlign = TextAlign.Center),
                ) {
                    withStyle(
                        style = SpanStyle(
                            color = Color.Black,
                            fontSize = 26.sp,
                            fontWeight = FontWeight.Bold,
                        ),
                    ) {
                        append("https://itmob.cn")
                    }
                }
            },
            topLeft = Offset.Zero.copy(y = 216.dp.toPx()),
            size = size,
        )

        // 绘制中间的应用图标
        icLauncher?.let {
            drawImage(
                image = it,
                topLeft = Offset(x = size.width/2-it.width/2, y = size.height/2-it.height/2),
            )
        }
    }
}

绘制沿弧形路径显示的文本使用的是 drawIntoCanvas 方法,而不是 Compose 提供的原生 API,因为当前版本(1.4.3)的 Jetpack Compose 还为提供 drawTextOnPath 类似的方法。

注意

细心的朋友可能留意到了在绘制圆弧时的代码:

val path = android.graphics.Path().apply {
    addArc(
        RectF(36.dp.toPx(), 36.dp.toPx(), 264.dp.toPx(), 264.dp.toPx()),
        90F,
        359.9F,
    )
}

代码中 addArc 方法的 sweepAngle 参数是 359.9F,而不是 360F,这是因为在 Android 5.0 API 21 中如果 sweepAngle 参数是 360 度,绘制图像时起始角度又变为了 0 度,这在绘制圆弧时可能并没有影响,但是我们需要让文字居中显示,就需要使圆弧路径的起始角度为 90 度,并让文字在路径中居中来实现。

所以 sweepAngle 使用了 359.9 而不是 360 度。

sweepAngle 为 360 度时,addArc 方法的 startAngle 参数不起作用

val path = android.graphics.Path().apply {
    addArc(
        RectF(36.dp.toPx(), 36.dp.toPx(), 264.dp.toPx(), 264.dp.toPx()),
        90F,
        360F,
    )
}

Stamp style text with error

其他

更多关于 Jetpack Compose 文本的介绍:

Jetpack Compose 中 DrawStyle 详解 - (线段/笔画/轮廓的绘制样式)

Jetpack Compose 中如何自定义文本笔画的描边效果

Jetpack Compose 中怎样隐藏或禁用 TextField(文本字段)的光标、文本选择手柄和文本工具栏?

怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?)

Jetpack Compose TextField VisualTransformation 视觉转换详解-高亮显示URL-格式化显示银行卡号码

怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?)

Jetpack Compose 怎样实现跑马灯效果(Marquee)

在 Jetpack Compose 中使用 DrawScope.drawText API 将文本绘制到 Canvas 上