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

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

How to draw custom stroke for text in Jetpack Compose?

1. 简介

从 Jetpack Compose 的 compose-ui1.4.0-alpha01 版本开始为 TextStyle, SpanStyle, Paragraph, MultiParagraph, 等增加了 DrawStyle 属性用于绘制文字笔画/轮廓效果。

更多关于 DrawStyle 的介绍可以查看另一篇文章:Jetpack Compose 中 DrawStyle 详解

2. 自定义笔画

Compose 中定义了两个 DrawStyle 的子类:FillStroke

  1. FilldrawStyle 属性的默认值,表示应使用所提供的颜色或图案完全填充形状。
  2. Stroke 是为带有笔画的绘制提供信息。

2.1 默认效果:Fill

Text(
    text = "https://itmob.cn",
    style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Red,
            fontSize = 32.sp,
            drawStyle = Fill, 
        ),
    ),
)

FilldrawStyle 属性的默认值,表示应使用所提供的颜色或图案完全填充形状。

DrawStyle: Fill

2.2 使用 Stroke 实现自定义效果

2.2.1 简单的描边效果

使用 Stroke 类型的 drawStyle 属性可以自定义文字的绘制样式,如指定笔画的宽度和笔画间的连接方式,可以实现简单的描边效果:

笔画的连接方式有三种:Miter 尖角/斜接, Round 圆角, Bevel 平角

Text(
    text = "itmob.cn",
    style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Red,
            fontSize = 86.sp,
            drawStyle = Stroke(width = 16f, join = StrokeJoin.Miter),
        ),
    ),
)
Text(
    text = "itmob.cn",
    style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Red,
            fontSize = 86.sp,
            drawStyle = Stroke(width = 16f, join = StrokeJoin.Round),
        ),
    ),
)
Text(
    text = "itmob.cn",
    style = LocalTextStyle.current.merge(
        TextStyle(
            color = Color.Red,
            fontSize = 86.sp,
            drawStyle = Stroke(width = 16f, join = StrokeJoin.Bevel),
        ),
    ),
)

Draw custom stroke for Text in JetpackCompose - join

本文中使用的是可组合项 Text 来绘制文字,我们也可以使用 drawText 方法在 DrawScope 中绘制,它们的用法相同,都是通过 drawType 属性来实现

Canvas(Modifier.fillMaxWidth().height(100.dp)) {
    drawText(
        textMeasurer = textMeasurer,
        text = "https://itmob.cn",
        style = TextStyle(
            fontSize = 36.sp,
            drawStyle = Stroke(
                width = 2.dp.toPx(),
                drawStyle = Stroke(width = 16f, join = StrokeJoin.Round),
            ),
        ),
    )
}

2.2.2 更复杂的描边效果

通过 PathEffect 实现更复杂的描边效果,通过 Path 类型的图形来绘制笔画。

源码中定义了四个方法来创建 PathEffect 对象。分别是:

  1. cornerPathEffect:将线段之间连接的锐角替换为指定半径的圆角
  2. dashPathEffect:将笔画绘制为具有给定间隔的一系列短划线/虚线
  3. stampedPathEffect:通过在绘制的笔画路径上盖指定的形状
  4. chainPathEffect:PathEffect 组合

关于 PathEffect 的更多介绍参见:pathEffect: PathEffect 应用于笔划的效果

那我们来看看通过 PathEffect 能实现怎样的笔画效果:

  1. 圆角描边的文本

    val radius = with(LocalDensity.current) { 16.dp.toPx() }
    val strokeWidth = with(LocalDensity.current) { 2.dp.toPx() }
    Text(
        text = "itmob.cn",
        style = LocalTextStyle.current.merge(
            TextStyle(
                color = Color.Red,
                fontSize = 86.sp,
                drawStyle = Stroke(
                    width = strokeWidth,
                    pathEffect = PathEffect.cornerPathEffect(radius),
                ),
            ),
        ),
    )
    

    Draw custom stroke for Text in JetpackComposecorner - PathEffect

  2. 虚线描边的文本

    Text(
        text = "itmob.cn",
        style = LocalTextStyle.current.merge(
            TextStyle(
                color = Color.Red,
                fontSize = 86.sp,
                drawStyle = Stroke(
                    width = strokeWidth,
                    pathEffect = PathEffect.dashPathEffect(
                        intervals = floatArrayOf(10F, 10F),
                        phase = 0F,
                    ),
                ),
            ),
        ),
    )
    

    Draw custom stroke for Text in JetpackComposecorner - dashPathEffect

  3. 使用 Path 类型的图形来描边的文本

    // 使用 Path 创建三角形图形,用三角形绘制文本笔画
    Text(
        text = "itmob.cn",
        style = LocalTextStyle.current.merge(
            TextStyle(
                color = Color.Red,
                fontSize = 86.sp,
                drawStyle = Stroke(
                    width = strokeWidth,
                    pathEffect = PathEffect.stampedPathEffect(
                        shape = Path().apply {
                            val size = 5F
                            moveTo(size / 2, 0F)
                            lineTo(0F, size)
                            lineTo(size, size)
                            lineTo(size / 2, 0F)
                            close()
                        },
                        advance = 10F,
                        phase = 0F,
                        style = StampedPathEffectStyle.Morph,
                    ),
                ),
            ),
        ),
    )
    

    Draw custom stroke for Text in JetpackComposecorner - stampedPathEffect

  4. 多个路径效果合并来描边的文本

    // 笔画使用圆角连接,并且显示为虚线
    val radius = with(LocalDensity.current) { 16.dp.toPx() }
    Text(
        text = "itmob.cn",
        style = LocalTextStyle.current.merge(
            TextStyle(
                color = Color.Red,
                fontSize = 86.sp,
                drawStyle = Stroke(
                    width = strokeWidth,
                    pathEffect = PathEffect.chainPathEffect(
                        outer = PathEffect.dashPathEffect(
                            intervals = floatArrayOf(10F, 10F),
                            phase = 0F,
                        ),
                        inner = PathEffect.cornerPathEffect(radius),
                    ),
                ),
            ),
        ),
    )
    

    Draw custom stroke for Text in JetpackComposecorner - chainPathEffect.jpeg

3. 总结

通过这个 DrawStyle 属性可以在 drawText 和 Text 等文本控件中实现自定义的文字描边/轮廓样式。

其他

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

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

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

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

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

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

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

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

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