Canvas 是 Jetpack Compose 中的一个 Composable 函数,可以使用 Canvas 来绘制各种形状,文本和路径。
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw))
- modifier:修饰外观和行为,Modifier 可用于设置大小,填充,对齐,背景颜色等属性。
- onDraw:执行具体的绘制操作。
drawLine
drawLine 用于绘制一条线,如下所示:
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
)
- color:线的颜色。
- start:线的起点坐标,使用 Offset 类型表示,即 (x, y) 坐标。
- end:线的终点坐标,同样使用 Offset 类型表示。
- strokeWidth:线的宽度。
- cap:线的端点样式,可以选择 StrokeCap.Butt,StrokeCap.Round 或 StrokeCap.Square 中的一种。
- pathEffect:路径效果,可以使用特定的路径效果对象,如绘制虚线可以用 PathEffect.dashPathEffect。
- alpha:线的透明度,取值范围为 0.0 到 1.0,0.0 表示完全透明,1.0 表示完全不透明。
- colorFilter:颜色过滤器,可以使用颜色过滤器对象来调整线的颜色。
- blendMode:用于指定绘制线条时所使用的混合模式,如 BlendMode.SrcOver、BlendMode.DstIn 等。
举个例子,绘制一条对角的虚线。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawLine(
color = Color.Red,
start = Offset(0f, 0f),
end = Offset(size.width, size.height),
strokeWidth = 6f,
cap = StrokeCap.Round,
// 20f表示虚线的宽度,10f表示间隔宽度,5f表示初始的偏移距离
pathEffect = PathEffect.dashPathEffect(floatArrayOf(20f, 10f), 5f)
)
}
}
drawRect 和 drawRoundRect
drawRect 用于绘制矩形,如下所示:
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:矩形的颜色
- topLeft:表示矩形左上角的坐标。默认值是 Offset.Zero,即画布的原点 (0, 0)。
- size:矩形的宽度和高度。
- alpha:透明度,范围在 0.0 到 1.0 之间。0.0 表示完全透明,1.0 表示完全不透明。
- style:绘制样式,可以是 Fill(填充矩形)或 Stroke(仅绘制矩形边框)。
- colorFilter:用于修改绘制内容的颜色。
- blendMode:混合模式,控制源颜色如何与目标颜色混合。
举个例子,绘制一个红色的矩形边框。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawRect(
color = Color.Red,
topLeft = Offset(50f, 50f),
size = Size(200f, 200f),
alpha = 0.5f,
style = Stroke(width = 10f)
)
}
}
如果想要绘制一个圆角的矩形,可以使用 drawRoundRect,绘制跟 drawRect 几乎无异,就是多了个 cornerRadius 参数表示圆角的半径,可以分别指定 x 轴和 y 轴的半径。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawRoundRect(
color = Color.Red,
topLeft = Offset(50f, 50f),
size = Size(200f, 200f),
alpha = 0.5f,
style = Stroke(width = 10f),
cornerRadius = CornerRadius(16f, 16f)
)
}
}
drawCircle
drawCircle 用于绘制圆形,如下所示:
fun drawCircle(
color: Color,
radius: Float = size.minDimension / 2.0f,
center: Offset = this.center,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
其中 center 是指圆心的位置,radius 是指半径大小。
举个例子,在屏幕中心绘制一个红色的圆。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.Red,
radius = 100f,
center = Offset(size.width / 2, size.height / 2)
)
}
}
drawOval
drawOval 用于绘制椭圆,如下所示:
fun drawOval(
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
)
举个例子,在 (100, 100) 位置绘制了一个红色的椭圆,宽度为 100,高度为 150。
@Composable
fun MyCanvas() {
//在 (100, 100) 位置绘制了一个红色的椭圆,宽度为 100,高度为 150。
Canvas(modifier = Modifier.fillMaxSize()) {
drawOval(
color = Color.Red,
topLeft = Offset(100f, 100f),
size = Size(100f, 150f)
)
}
}
drawArc
drawArc 用于绘制弧形或扇形,如下所示:
fun drawArc(
color: Color,
startAngle: Float,
sweepAngle: Float,
useCenter: Boolean,
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
)
- startAngle:表示弧线的起始角度,以度为单位,0 度表示水平向右方向,顺时针增加角度。例如:0f 表示从水平向右方向开始,90f 表示从垂直向上方向开始。
- sweepAngle:表示弧线的扫过的角度,以度为单位。正值表示顺时针方向,负值表示逆时针方向。例如:90f 表示从起始角度开始顺时针旋转 90 度。
- useCenter: 表示是否连接弧线的起点和终点以形成一个封闭的区域。如果为 true,则会连接起点和终点,形成一个扇形,如果为 false,则只绘制弧线而不连接起点和终点。
举个例子,绘制一个180度的扇形。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
drawArc(
color = Color.Red,
startAngle = 0f,
sweepAngle = 180f,
useCenter = true,
topLeft = Offset(100f, 100f),
size = Size(150f, 150f),
style = Stroke(width = 10f),
blendMode = BlendMode.SrcOver
)
}
}
drawImage
drawImage 用于绘制图片,如下所示:
fun drawImage(
image: ImageBitmap,
srcOffset: IntOffset = IntOffset.Zero,
srcSize: IntSize = IntSize(image.width, image.height),
dstOffset: IntOffset = IntOffset.Zero,
dstSize: IntSize = srcSize,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode,
filterQuality: FilterQuality = DefaultFilterQuality
)
- image:要绘制的位图图像
- srcOffset:源图像中裁剪区域的左上角偏移
- srcSize:源图像中裁剪区域的大小
- dstOffset:目标画布上绘制位置的左上角偏移
- dstSize:目标画布上绘制区域的大小
举个例子,在画布的 (150, 150) 位置绘制,并将其缩放到 200x200 像素上,完整的显示图片。
@Composable
fun MyCanvas() {
val imageBitmap = ImageBitmap.imageResource(id = R.drawable.test_img)
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
image = imageBitmap,
dstOffset = IntOffset(150, 150),
dstSize = IntSize(200, 200),
)
}
}
如果此时想从源图像的 (20, 20) 位置裁剪出一个 100x100 像素的区域,可以这样干:
@Composable
fun MyCanvas() {
val imageBitmap = ImageBitmap.imageResource(id = R.drawable.test_img)
Canvas(modifier = Modifier.fillMaxSize()) {
drawImage(
image = imageBitmap,
srcOffset = IntOffset(20, 20),
srcSize = IntSize(100, 100),
dstOffset = IntOffset(150, 150),
dstSize = IntSize(200, 200),
)
}
}
drawPath
drawPath 用于绘制路径,如下所示:
fun drawPath(
path: Path,
color: Color,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
path 就是要绘制的路径对象,举个例子,画一个闪电线。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
val path = Path().apply {
moveTo(200f, 0f) //移动到起点
lineTo(100f, 100f) //从起点画一条线到(100, 100)
lineTo(300f, 100f) //再画一条线到(300, 100)
lineTo(100f, 300f) //再画一条线到(100, 300)
}
drawPath(
path = path,
color = Color.Red,
style = Stroke(width = 6f)
)
}
}
如果想闭合路径,回到起点的话,可以最后加上 close()。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
val path = Path().apply {
moveTo(200f, 0f) //移动到起点
lineTo(100f, 100f) //从起点画一条线到(100, 100)
lineTo(300f, 100f) //再画一条线到(300, 100)
lineTo(100f, 300f) //再画一条线到(100, 300)
close()
}
drawPath(
path = path,
color = Color.Red,
style = Stroke(width = 6f) // 填充路径
)
}
}
drawPoints
drawPoints 用于绘制点,如下所示:
fun drawPoints(
points: List<Offset>,
pointMode: PointMode,
color: Color,
strokeWidth: Float = Stroke.HairlineWidth,
cap: StrokeCap = StrokeCap.Butt,
pathEffect: PathEffect? = null,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
- points:这是一个包含 Offset 对象的列表,表示要绘制的点的位置。
- pointMode:绘制点的模式。PointMode.Points 表示单独绘制每个点,PointMode.Lines 表示绘制相邻点之间的连线。
- color:指定绘制点或线的颜色。
- strokeWidth:绘制线条时使用的宽度。
- cap: 指定线条的端点样式。StrokeCap.Butt 表示端点是平直的,StrokeCap.Round 表示端点是圆形的StrokeCap.Square 表示端点是方形的。
举个例子,绘制一些平行于 X 轴的点和线。
@Composable
fun MyCanvas() {
Canvas(modifier = Modifier.fillMaxSize()) {
val points = listOf(
Offset(100f, 100f),
Offset(200f, 100f),
Offset(300f, 100f),
Offset(400f, 100f),
Offset(500f, 100f)
)
//绘制单个点集合
drawPoints(
points = points,
pointMode = PointMode.Points,
color = Color.Red,
strokeWidth = 20f,
cap = StrokeCap.Round,
)
val connectedPoints = listOf(
Offset(100f, 200f),
Offset(200f, 200f),
Offset(300f, 200f),
Offset(400f, 200f),
Offset(500f, 200f)
)
//绘制连接线
drawPoints(
points = connectedPoints,
pointMode = PointMode.Lines,
color = Color.Red,
strokeWidth = 6f,
cap = StrokeCap.Round,
)
}
}
drawText
drawText 用于绘制文字,如下所示:
@ExperimentalTextApi
fun DrawScope.drawText(
textLayoutResult: TextLayoutResult,
color: Color = Color.Unspecified,
topLeft: Offset = Offset.Zero,
alpha: Float = Float.NaN,
shadow: Shadow? = null,
textDecoration: TextDecoration? = null,
drawStyle: DrawStyle? = null,
blendMode: BlendMode = DrawScope.DefaultBlendMode
)
- textLayoutResult:文本布局的结果,它包含了绘制文本的信息。
- color:文本的颜色,如果不指定,将使用 TextLayoutResult 中设定的颜色。
- topLeft:文本绘制的起始位置(左上角)。
- shadow:文本的阴影效果。
- textDecoration:文本的装饰,如 TextDecoration.Underline(下划线)或 TextDecoration.LineThrough(删除线)
举个例子,绘制一段带阴影的文字。
@OptIn(ExperimentalTextApi::class)
@Composable
fun MyCanvas() {
val textMeasurer = rememberTextMeasurer()
val textLayoutResult = textMeasurer.measure(
text = "Hello, Compose!",
style = TextStyle(fontSize = 20.sp),
)
Canvas(modifier = Modifier.fillMaxSize()) {
drawText(
textLayoutResult = textLayoutResult,
color = Color.Red,
topLeft = Offset(50f, 50f),
shadow = Shadow(
color = Color.Gray, //阴影颜色
offset = Offset(6f, 6f),//偏移量
blurRadius = 4f //阴影模糊半径
),
textDecoration = TextDecoration.Underline,
blendMode = DrawScope.DefaultBlendMode
)
}
}