在Compose中,作用域的概念随处可见,自定义图形也一样。下面一段代码,创建一个Canvas的作用域,并讲其范围指定为父元素中的所有可用空间:
Canvas(modifier = Modifier.fillMaxSize()) {
}
一、绘制图形
- drawLine:绘制直线
- drawCircle:绘制圆形
- drawRect:绘制矩形
1. drawLine(绘制直线)
| 参数 | 类型 | 描述 | 其他说明 |
|---|---|---|---|
| brush | Brush | 线条颜色组合 | 可用于绘制渐变色,与color属性二选一 |
| color | Color | 线条颜色 | |
| start | Offset | 线条起始坐标 | |
| end | Offset | 线条终止坐标 | |
| strokeWidth | Float | 线条宽度 | |
| cap | StrokeCap | 线两头的样式 | Butt:无头 Round:圆头 Square:方头 |
| pathEffect | PathEffect? | 设置线的显示效果 | cornerPathEffect:连接线段之间的夹角用一种更平滑的方式连接 dashPathEffect:将线段虚线化 chainPathEffect:可分别设置外部效应和内部效应的路径 stampedPathEffect:使用特定的形状来绘制路径 |
| alpha | Float | 透明度 | 取值:[0.0, 1.0] |
| colorFilter | ColorFilter? | 颜色过滤器 | |
| blendMode | BlendMode | 混合模式 | 这个后面再说 |
@Composable
fun CanvasLine() {
val pathList: List<Pair<PathEffect, String>> = listOf(
Pair(PathEffect.cornerPathEffect(6f), "cornerPathEffect"),
Pair(PathEffect.dashPathEffect(intervals = floatArrayOf(25f, 25f, 5f, 10f)), "dashPathEffect")
)
val strokeWidth = remember { mutableStateOf(20) }
val color = remember { mutableStateOf(Color.Blue) }
val cap = remember { mutableStateOf(StrokeCap.Butt) }
val alpha = remember { mutableStateOf(1f) }
val pathEffect = remember { mutableStateOf(pathList[0].first) }
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
......
Canvas(
modifier = Modifier
.width(200.dp)
.height(400.dp),
onDraw = {
val canvasWidth = size.width
val canvasHeight = size.height
drawLine(
color = color.value,
start = Offset(x = canvasWidth * 3 / 4, y = canvasHeight / 4),
end = Offset(x = canvasWidth / 4, y = canvasHeight * 3 / 4),
strokeWidth = strokeWidth.value.toFloat(),
cap = cap.value,
pathEffect = pathEffect.value,
alpha = alpha.value
)
})
}
}
2. drawCircle(绘制圆形)
| 参数 | 类型 | 描述 | 其他说明 |
|---|---|---|---|
| brush | Brush | 线条颜色组合 | 可用于绘制渐变色,与color属性二选一 |
| color | Color | 线条颜色 | |
| radius | Float | 半径 | 默认为Canvas的宽与高之中较小的值 |
| center | Offset | 圆心偏移量 | 默认值在Canvas区域的中心点 |
| alpha | Float | 透明度 | 取值:[0.0, 1.0] |
| style | DrawStyle | 图形的绘制风格 | Fill:实心 Stroke:空心 |
@Composable
fun CanvasCircle() {
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
val color = remember { mutableStateOf(Color.Blue) }
val radiusParent = remember { mutableStateOf(1f) }
val centerXOffset = remember { mutableStateOf(0) }
val centerYOffset = remember { mutableStateOf(0) }
val alpha = remember { mutableStateOf(1f) }
val drawStyle: MutableState<DrawStyle> = remember { mutableStateOf(Fill) }
......
Canvas(
modifier = Modifier.size(200.dp),
onDraw = {
drawCircle(
color = color.value,
radius = size.minDimension * radiusParent.value / 2.0f,
center = Offset(this.center.x + centerXOffset.value, this.center.y + centerYOffset.value),
alpha = alpha.value,
style = drawStyle.value
)
})
}
}
3. drawRect(绘制矩形)
| 参数 | 类型 | 描述 | 其他说明 |
|---|---|---|---|
| brush | Brush | 线条颜色组合 | 可用于绘制渐变色,与color属性二选一 |
| color | Color | 线条颜色 | |
| topLeft | Offset | 左上角的偏移量 | 默认为(0, 0) |
| size | Size | 矩形的尺寸 | 默认为Canvas的尺寸 |
| alpha | Float | 透明度 | 取值:[0.0, 1.0] |
| style | DrawStyle | 图形的绘制风格 | Fill:实心 Stroke:空心 |
@Composable
fun CanvasRect() {
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
val color = remember { mutableStateOf(Color.Blue) }
val topLeftXOffset = remember { mutableStateOf(0) }
val topLeftYOffset = remember { mutableStateOf(0) }
val width = remember { mutableStateOf(200) }
val height = remember { mutableStateOf(200) }
val alpha = remember { mutableStateOf(1f) }
val drawStyle: MutableState<DrawStyle> = remember { mutableStateOf(Fill) }
......
Canvas(
modifier = Modifier.size(200.dp),
onDraw = {
drawRect(
color = color.value,
topLeft = Offset(topLeftXOffset.value.toFloat(), topLeftYOffset.value.toFloat()),
size = Size(width = width.value.toFloat(), height = height.value.toFloat()),
alpha = alpha.value,
style = drawStyle.value
)
})
}
}
4. drawArc(绘制弧线)
| 参数 | 类型 | 描述 | 其他说明 |
|---|---|---|---|
| brush | Brush | 线条颜色组合 | 可用于绘制渐变色,与color属性二选一 |
| color | Color | 线条颜色 | |
| startAngle | Float | 起始角度 | 当取值为0时,起始角度为时钟3点钟方向的位置; 当取值为a时,起始角度为时钟3点钟方向按顺时针方向a度的位置。 |
| sweepAngle | Float | 持续角度 | 弧线持续的角度(顺时针方向为正值) |
| useCenter | Boolean | 是否绘制弦 | |
| topLeft | Offset | 左上角的偏移量 | 默认为(0, 0) |
| size | Size | 尺寸 | 默认为Canvas的尺寸 |
| alpha | Float | 透明度 | 取值:[0.0, 1.0] |
| style | DrawStyle | 图形的绘制风格 | Fill:实心 Stroke:空心 |
@Composable
fun CanvasArc() {
val color = remember { mutableStateOf(Color.Blue) }
val startAngle = remember { mutableStateOf(0f) }
val sweepAngle = remember { mutableStateOf(180f) }
val useCenter = remember { mutableStateOf(false) }
val topLeftXOffset = remember { mutableStateOf(0) }
val topLeftYOffset = remember { mutableStateOf(0) }
val width = remember { mutableStateOf(200) }
val height = remember { mutableStateOf(200) }
val alpha = remember { mutableStateOf(1f) }
val drawStyle: MutableState<DrawStyle> = remember { mutableStateOf(Stroke(width = 8f)) }
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
......
Canvas(
modifier = Modifier
.background(Color.LightGray)
.width(200.dp)
.height(200.dp),
onDraw = {
drawArc(
color = color.value,
startAngle = startAngle.value,
sweepAngle = sweepAngle.value,
useCenter = useCenter.value,
topLeft = Offset(topLeftXOffset.value.toFloat(), topLeftYOffset.value.toFloat()),
size = Size(width.value.dp.toPx(), height.value.dp.toPx()),
alpha = alpha.value,
style = drawStyle.value
)
})
}
}
5. drawPath(绘制路径)
| 参数 | 类型 | 描述 | 其他说明 |
|---|---|---|---|
| path | Path | 路径 | 绘制的路径 |
| brush | Brush | 线条颜色组合 | 可用于绘制渐变色,与color属性二选一 |
| color | Color | 线条颜色 | |
| alpha | Float | 透明度 | 取值:[0.0, 1.0] |
| style | DrawStyle | 图形的绘制风格 | Fill:实心 Stroke:空心 |
@Composable
fun CanvasCustom() {
Column(modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally) {
val color = remember { mutableStateOf(Color.Blue) }
val alpha = remember { mutableStateOf(1f) }
val drawStyle: MutableState<DrawStyle> = remember { mutableStateOf(Stroke(width = 8f)) }
......
Canvas(
modifier = Modifier
.background(Color.LightGray)
.size(200.dp),
onDraw = {
//绘制一个五角星
//指定五角星的5个顶点坐标
val point1 = Offset(100.dp.toPx(), 0.dp.toPx())
val point2 = Offset(38.dp.toPx(), 200.dp.toPx())
val point3 = Offset(200.dp.toPx(), 78.dp.toPx())
val point4 = Offset(0.dp.toPx(), 78.dp.toPx())
val point5 = Offset(162.dp.toPx(), 200.dp.toPx())
drawPath(
path = Path().apply {
//移动到起始点
moveTo(point1.x, point1.y)
//绘制直线
lineTo(point2.x, point2.y)
lineTo(point3.x, point3.y)
lineTo(point4.x, point4.y)
lineTo(point5.x, point5.y)
//封闭图形(回到起始点)
close()
},
color = color.value,
alpha = alpha.value,
style = drawStyle.value
)
})
}
}
二、图形变换
| 属性 | 描述 | 其他说明 |
|---|---|---|
| scale | 缩放 | 参数中的scale、scaleX、scaleY均表示缩放的倍数 |
| translate | 平移 | 参数中的left、top分别表示向左/向上平移的像素 |
| rotate | 旋转 | 参数中degrees表示按顺时针选转的角度,pivot表的旋转的中心点 |
| inset | 边衬区 | 可更改绘制边界和平移绘制,为绘制的图形添加内边距 |
| withTransform | 组合多个变换 |
Canvas(
modifier = Modifier.size(200.dp)
) {
withTransform({
scale(scale = scale.value, pivot = center)
translate(left = translateX.value, top = translateY.value)
rotate(degrees = degrees.value, pivot = center)
}) {
drawImage(image = imageBitmap)
}
}
只需要单独使用其中一种变换方式的话,可以这样:
scale(scale = scale.value, pivot = center) {
drawImage(image = imageBitmap)
}
translate(left = translateX.value, top = translateY.value) {
drawImage(image = imageBitmap)
}
rotate(degrees = degrees.value, pivot = center) {
drawImage(image = imageBitmap)
}
三、渐变Brush
颜色渐变的方式有很多种,比如线性渐变,发散渐变等。各种方式都有着对应的api,但总结起来所需要的参数无非就是如下三种:颜色集合、起始位置、平铺模式。
fun xxxGradient(颜色集合, 颜色集合作用的起始范围, tileMode(平铺模式))
-
颜色集合:在起始范围内的颜色渐变顺序。其传参类型有两种选择,
Pair<Float, Color>和List<Color>,前者可根据Float的数值对每个颜色的占比进行分配,后者表示每个颜色渐变中所在的位置是以起始位置(0)到终止位置(1)进行均分的位置。//起始位置为黄色,到20%的位置渐变为红色,再到结束的位置渐变为蓝色 val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) //如下两种方式等价: //起始位置为黄色,到20%的位置渐变为红色,再到结束的位置渐变为蓝色 val colors = listOf(Color.Yellow, Color.Red, Color.Blue) val colors = arrayOf( 0.0f to Color.Yellow, 0.5f to Color.Red, 1f to Color.Blue ) -
tileMode:可选值有
Clamp、Repeated、Mirror、Decal。- Clamp:边缘固定为最终的颜色。然后,它会将区域的剩余空间绘制为距离最近的颜色。
- Repeated:边缘按从最后一种颜色到第一种颜色的顺序镜像。
- Mirror:边缘按从最后一种颜色到第一种颜色的顺序镜像。
- Decal:仅在边界大小的范围内渲染。
TileMode.Decal利用透明的黑色对原始边界以外的内容进行采样,而TileMode.Clamp对边缘颜色进行采样。
-
方向
- linearGradient:线性
- horizontalGradient:横向
- verticalGradient:纵向
- radialGradient:径向
- sweepGradient:发散