《Jetpack Compose系列学习》-18 Compose中Canvas绘制线和矩形

1,124 阅读5分钟
绘制线

之前我们学习了Canvas绘制点,我们再看看线怎么画。两点确定一条直线,和之前绘制点一样,绘制线的方法也是在DrawScope中定义,如下所示:

fun drawLine(
    color: Color, // 颜色
    start: Offset, // 线段开始的坐标点
    end: Offset, // 线段结束的坐标点
    strokeWidth: Float = Stroke.HairlineWidth, // 线段宽度
    cap: StrokeCap = Stroke.DefaultCap, // 处理线段的末端
    pathEffect: PathEffect? = null, // 适用于线段的可选效果或图案
    /*FloatRange(from = 0.0, to = 1.0)*/
    alpha: Float = 1.0f, // 透明度
    colorFilter: ColorFilter? = null, // 颜色效果
    blendMode: BlendMode = DefaultBlendMode // 混合模式
)

绘制线的方法和绘制点的方法很像,就是多了start和end,二者类型都是Offset,用来确定线段两端的坐标,其他参数和绘制点的没啥区别,我们来看看绘制线段的一个示例:

@Composable
fun DrawLineTest() {
    val start = Offset(100f, 100f)
    val end = Offset(900f, 900f)
    Canvas(modifier = Modifier.size(360.dp)) {
        drawLine(
            color = Color.Red,
            start = start,
            end = end,
            strokeWidth = 30f,
            cap = StrokeCap.Round
        )
    }
}

我们定义了两个点,是线段的起始点,然后定义了一个Canvas可组合项,其中使用DSL的方法绘制线段,将线段颜色定义为红色,线段宽度为30,末端做了圆角处理,效果如下:

image.png

还是比较简单的。

绘制矩形

绘制矩形之前来想一个问题,在一张白纸上画一个矩形需要什么条件?需要知道起始点,还需要知道矩形的大小,是的,我们直接看看绘制矩形方法的参数都有哪些:

fun drawRect(
    color: Color, // 颜色
    topLeft: Offset = Offset.Zero, // 起始点(左上角点的位置)
    size: Size = this.size.offsetSize(topLeft), // 大小
    /*@FloatRange(from = 0.0, to = 1.0)*/
    alpha: Float = 1.0f, // 透明度
    style: DrawStyle = Fill, // 样式
    colorFilter: ColorFilter? = null, // 颜色效果
    blendMode: BlendMode = DefaultBlendMode // 混合模式
)

color是必须填写的,其中还有另外一个绘制方法中,brush替代了这里的color,都和颜色相关,上篇中已经介绍过了,这里就不说了。我们先绘制一个矩形看看:

@Composable
fun DrawRectTest() {
    val topLeft = Offset(100f, 100f)
    Canvas(modifier = Modifier.size(360.dp)) {
        drawRect(
            color = Color.Red,
            topLeft = topLeft,
            size = Size(400f, 600f)
        )
    }
}

能看到,矩形的左上角点的位置(起点)是(100、100);颜色是红色,大小宽高是400、600;看下效果:

image.png

有人会好奇,绘制矩形中有一个参数style之前没遇到过,我们一起看看,它的类型是DrawStyle,就是矩形是否有描边或填充,看看DrawStyle源码:

sealed class DrawStyle

/**
 * 绘制时完全用提供的颜色或者图案填充
 */
object Fill : DrawStyle()

class Stroke(
    /**
     * 画笔的宽度(单位:像素)
     */
    val width: Float = 0.0f,

    /**
     * 画笔斜角值,当连接角度很锐利时,此参数用于控制斜角连接的角度,值必须>=0
     */
    val miter: Float = DefaultMiter,

    /**
     * 线段末端的样式
     */
    val cap: StrokeCap = StrokeCap.Butt,

    /**
     * 设置直线和曲线段在描述路径上连接的处理方式,默认值为StrokeJoin.Miter
     */
    val join: StrokeJoin = StrokeJoin.Miter,

    /**
     * 作用于笔画的效果,null表示要绘制实线
     */
    val pathEffect: PathEffect? = null
) : DrawStyle() {
    // 省略...
}

DrawStyle是一个密封类,有两个子类Fill和Stoke,前者表示矩形整个被填充,后者表示矩形被描边处理。在Stoke中可以设置一些参数,大部分参数我们都比较熟悉了,需要注意的是,这里的width和前面的一样,默认值也是0.0f,所以必须进行设置,否则就空白无内容。我们一起绘制一个描边空心的矩形:

@Composable
fun DrawRectTest() {
    val topLeft = Offset(100f, 100f)
    val rectSize = Size(400f, 600f)
    Canvas(modifier = Modifier.size(360.dp)) {
        drawRect(
            color = Color.Red,
            topLeft = topLeft,
            size = rectSize,
            style = Stroke(
                width = 30f,
                miter = 4f,
                cap = StrokeCap.Round
            )
        )
    }
}

style我们设置为Stroke,width为30f,miter为4f,末端圆角处理,效果如下:

image.png

Stroke中的参数join之前没有见过,它类型为StrokeJoin,用来设置直线和曲线段在描边路径上连接的处理方式,默认值为StrokeJoin.Miter。先看一些StrokeJoin类:

@Suppress("INLINE_CLASS_DEPRECATED", "EXPERIMENTAL_FEATURE_WARNING")
@Immutable
inline class StrokeJoin internal constructor(@Suppress("unused") private val value: Int) {
    companion object {
        /**
         * 线段之间的连接形成尖角
         */
        val Miter = StrokeJoin(0)

        /**
         * 线段之间的连接是半圆形的
         */
        val Round = StrokeJoin(1)

        /**
         * 将线段对接端的角连接起来,呈现斜角外观
         */
        val Bevel = StrokeJoin(2)
    }

    override fun toString() = when (this) {
        Miter -> "Miter"
        Round -> "Round"
        Bevel -> "Bevel"
        else -> "Unknown"
    }
}

StrokeJoin类里有一个伴生对象,有三个变量:Miter、Round和Bevel,其默认值为Miter,就是类似于上图所示的尖角,我们修改下上面示例代码中的join:

@Composable
fun DrawRectTest() {
    val topLeft = Offset(100f, 100f)
    val rectSize = Size(400f, 600f)
    Canvas(modifier = Modifier.size(360.dp)) {
        drawRect(
            color = Color.Red,
            topLeft = topLeft,
            size = rectSize,
            style = Stroke(
                width = 30f,
                miter = 4f,
//                cap = StrokeCap.Round
                join = StrokeJoin.Round
            )
        )
    }
}

image.png

可以看到,矩形线段连接的地方变得圆滑了。绘制矩形时一定要控制好起始点和矩形的大小,再根据需求选择填充矩形还是进行描边等处理。

绘制圆角矩形

我们再看看圆角矩形怎么绘制,看绘制圆角矩形方法的定义:

fun drawRoundRect(
    color: Color,
    topLeft: Offset = Offset.Zero,
    size: Size = this.size.offsetSize(topLeft),
    cornerRadius: CornerRadius = CornerRadius.Zero,
    style: DrawStyle = Fill,
    /*@FloatRange(from = 0.0, to = 1.0)*/
    alpha: Float = 1.0f,
    colorFilter: ColorFilter? = null,
    blendMode: BlendMode = DefaultBlendMode
)

可以看到,参数和绘制普通矩形基本一样,只是多了一个圆角大小的参数cornerRadius,默认值是CornerRadius,可以通过下面的方法直接进行设置:

@Stable
fun CornerRadius(x: Float, y: Float = x) = CornerRadius(packFloats(x, y))

该方法有两个参数x、y,都是Float类型,分别针对x轴和y轴的半径大小构造一个Radius,需要注意的是x必须设置,y可以不设置,y不设置的话,其默认值为x。那么我们就一起绘制一个圆角矩形吧:

@Composable
fun DrawRoundRectTest() {
    val topLeft = Offset(100f, 100f)
    val rectSize = Size(400f, 600f)
    Canvas(modifier = Modifier.size(360.dp)) {
        drawRoundRect(
            color = Color.Red,
            topLeft = topLeft,
            size = rectSize,
            cornerRadius = CornerRadius(50f), // 圆角半径大小
            style = Stroke(
                width = 30f,
                miter = 4f,
                join = StrokeJoin.Round
            )
        )
    }
}

image.png

可以看到圆角效果还是非常明显的,大家在使用圆角矩形的时候可以根据实际需求来设置圆角值,其它使用方法和矩形一样。

相关代码已上传到github: github.com/Licarey/com…