本文目的是拆解Google Android官方Compose项目JetLagged中曲线图的实现过程,JetLagged项目地址。文章中的代码是我自己实现过程总结,和官方的案例代码会有一些出入,请注意。
最终效果
知识点
- 如何使用
Brush绘制渐变色 - 如何使用
Path绘制曲线
前期准备
准备需要的颜色
在Color.kt文件中写入以下颜色:
val Pink = Color(0xFFEAA8A9)
val Purple = Color(0xFFD2B4D3)
val Green = Color(0xFFADD7B9)
创建一份虚拟数据
val fakeData = arrayOf(
PointF(30f, 100f),
PointF(50f, 30f),
PointF(70f, 60f),
PointF(90f, 100f),
PointF(110f, 30f),
PointF(130f, 60f),
PointF(150f, 10f),
PointF(170f, 20f),
PointF(180f, 50f),
PointF(190f, 80f),
PointF(200f, 60f),
PointF(210f, 90f),
PointF(230f, 100f),
PointF(250f, 30f),
PointF(260f, 60f),
PointF(280f, 10f),
PointF(300f, 90f),
PointF(320f, 75f),
PointF(340f, 30f),
PointF(360f, 10f),
)
步骤一:简单地绘制并连接每个点
我们先简单地使用Path将每个点用直线连接起来,并且设置一个固定的颜色和10px的线条宽度:
@Composable
fun WaveGraph() {
Box(
Modifier
.fillMaxWidth()
.height(100.dp)
.drawWithCache {
val path = Path()
fakeData.forEach { point ->
val x = point.x.dp.toPx()
val y = point.y.dp.toPx()
path.lineTo(x, y)
}
onDrawBehind {
drawPath(path, SolidColor(Pink), style = Stroke(10f))
}
}
)
}
效果图如下:
步骤二:绘制曲线
接下来我们将lineTo()替换掉,使用Path的cubicTo()将直线修改成曲线:
@Composable
fun WaveGraph() {
Box(
Modifier
.fillMaxWidth()
.height(100.dp)
.drawWithCache {
val path = Path()
var previousX = 0f // ADD
var previousY = 0f // ADD
fakeData.forEach { point ->
val x = point.x.dp.toPx()
val y = point.y.dp.toPx()
// ADD
val controlPoint1 = PointF((x + previousX) / 2f, previousY)
val controlPoint2 = PointF((x + previousX) / 2f, y)
path.cubicTo(
controlPoint1.x,
controlPoint1.y,
controlPoint2.x,
controlPoint2.y,
x,
y
)
previousX = x
previousY = y
}
onDrawBehind {
drawPath(path, SolidColor(Pink), style = Stroke(10f))
}
}
)
}
💡cubicTo()是根据贝赛尔曲线原理绘制曲线的,关于贝赛尔曲线的知识请自行学习
controlPoint1和controlPoint2是绘制贝赛尔曲线时的两个用于辅助的点,并不会绘制在屏幕上。
效果图如下:
步骤三:增加渐变色
现在离最终效果已经非常接近了,让我们再修改一下颜色即可。先定义一个array,确定不同颜色在图形上的分布,这里的颜色在一开始为Pink,到图形中间时会过渡到Purple,到图形末尾过渡到Green:
@Composable
fun WaveGraph() {
// ADD
val waveColorStops = remember {
arrayOf(
0f to Pink,
0.5f to Purple,
1f to Green
)
}
Box(
Modifier
.fillMaxWidth()
.height(100.dp)
.drawWithCache {
}
)
}
然后我们自定义一个渐变Brush,我们的渐变色是在垂直方向上过渡的,也支持定义其它方向的过渡色:
@Composable
fun WaveGraph() {
val waveColorStops = remember {
arrayOf(
0f to Pink,
0.5f to Purple,
1f to Green
)
}
Box(
Modifier
.fillMaxWidth()
.height(100.dp)
.drawWithCache {
// ADD
val gradientBrush = Brush.verticalGradient(
colorStops = waveColorStops,
startY = 0f,
endY = size.height
)
}
)
}
最后在drawPath()时将渐变gradientBrush作为参数传入即可:
onDrawBehind {
drawPath(path, gradientBrush, style = Stroke(10f)) // ADD
}
来看一下最终效果:
完整代码如下:
@Composable
fun WaveGraph() {
val waveColorStops = remember {
arrayOf(
0f to Pink,
0.5f to Purple,
1f to Green
)
}
Box(
Modifier
.fillMaxWidth()
.height(100.dp)
.drawWithCache {
val gradientBrush = Brush.verticalGradient(
colorStops = waveColorStops,
startY = 0f,
endY = size.height
)
val path = Path()
var previousX = 0f
var previousY = 0f
fakeData.forEach { point ->
val x = point.x.dp.toPx()
val y = point.y.dp.toPx()
val controlPoint1 = PointF((x + previousX) / 2f, previousY)
val controlPoint2 = PointF((x + previousX) / 2f, y)
path.cubicTo(
controlPoint1.x,
controlPoint1.y,
controlPoint2.x,
controlPoint2.y,
x,
y
)
previousX = x
previousY = y
}
onDrawBehind {
drawPath(path, gradientBrush, style = Stroke(10f))
}
}
)
}