自定义View在Android开发中很常见,对于一些复杂的业务需求我们需要继承现有的基础View去实现更复杂的View。Google想尽办法让Compose中的自定义View简单方便些,首先我们从这几个点去学习自定义View:
- 了解Compose中的Canvas
- 在Compose中绘制文字、曲线、矩形和路径等
- 使用混合模式————BlendMode
我们今天先学习:点Point的绘制。
了解Compose中的Canvas
我们先学习Canvas的基本概念和工作流程,在Android View中我们自定义View中的绘制方式最常见的就是重写其onDraw方法,我们先看看实例代码:
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// 绘制操作
}
}
先继承一个View,然后在onDraw方法中进行绘制操作,里面有一个Canvas参数,它可以绘制点、线、圆等,还可以使用不同的控制方法来绘制内容的覆盖关系。
通过上面的内容我们知道Canvas在自定义View中扮演者关键的角色,同样,在Compose中也是非常重要,来看看Compose中应该怎么使用Canvas:
@Composable
fun CustomViewTest() {
Canvas(modifier = Modifier.fillMaxSize()) {
// 省略
}
}
可以看到在Compose中使用Canvas和在Android View中使用Canvas的方式完全不同,其实Compose中的Canvas也是一个可组合项。在Compose中万物皆可组合项,来看看Canvas源码:
@Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw))
可以看到Canvas有两个参数,一个是我们很熟悉的修饰符Modifier,另外一个是onDraw,类型是DrawScope块(DrawScope类型的函数引用),和之前的LazyListScope有点像儿,我们之前学过的LazyColumn,想要使用DSL描述列表的内容就离不开其参数LazyListScope块。这里的DrawScope块的作用与之相同,先看看DrawScope接口的定义:
@DrawScopeMarker
interface DrawScope : Density {
/**
* 当前的DrawContext,包含创建绘图上下文环境所需的依赖项
*/
val drawContext: DrawContext
/**
* 绘制上下文环境当前边界的中心
*/
val center: Offset
get() = drawContext.size.center
/**
* 当前绘制上下文环境的大小(可以通过size获取当前Canvas的宽高)
*/
val size: Size
get() = drawContext.size
/**
* 绘制的方向
*/
val layoutDirection: LayoutDirection
/**
* 绘制线
*/
fun drawLine(
brush: Brush,
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
)
/**
* 绘制线
*/
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
)
/**
* 绘制矩形
*/
fun drawRect(
brush: Brush,
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
)
/**
* 绘制矩形
*/
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
)
/**
* 绘制图片
*/
fun drawImage(
image: ImageBitmap,
topLeft: Offset = Offset.Zero,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
/**
* 绘制图片
*/
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
)
/**
* 绘制圆角矩形
*/
fun drawRoundRect(
brush: Brush,
topLeft: Offset = Offset.Zero,
size: Size = this.size.offsetSize(topLeft),
cornerRadius: CornerRadius = CornerRadius.Zero,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
/**
* 绘制圆角矩形
*/
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
)
/**
*绘制圆
*/
fun drawCircle(
brush: Brush,
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
)
/**
* 绘制圆
*/
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
)
/**
* 绘制椭圆
*/
fun drawOval(
brush: Brush,
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
)
/**
* 绘制椭圆
*/
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
)
/**
* 绘制圆弧
*/
fun drawArc(
brush: Brush,
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
)
/**
* 绘制圆弧
*/
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
)
/**
* 绘制路径
*/
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
)
/**
* 绘制路径
*/
fun drawPath(
path: Path,
brush: Brush,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f,
style: DrawStyle = Fill,
colorFilter: ColorFilter? = null,
blendMode: BlendMode = DefaultBlendMode
)
/**
* 绘制多个点
*/
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
)
/**
* 绘制多个点
*/
fun drawPoints(
points: List<Offset>,
pointMode: PointMode,
brush: Brush,
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
)
/**
* 偏移操作
*/
private fun Size.offsetSize(offset: Offset): Size =
Size(this.width - offset.x, this.height - offset.y)
companion object {
/**
* 用于每个绘图操作的默认混合模式,可以确保将内容绘制在目标中的像素上方
*/
val DefaultBlendMode: BlendMode = BlendMode.SrcOver
}
有些绘制方法具体使用会在下面介绍,先看看DrawScope,它是一个接口,并用@DrawScopeMarker注解修饰(一个DSL标记,用于区分绘图操作和画布转换操作);它继承自Density接口(其中定义了Dp、Px、Int和TextUnit之间的转换);接口内含有4个全局变量;伴生对象中有一个变量,表示绘制操作默认的混合模式,其中混合模式就是在画布上绘制时使用的算法。
Canvas绘制点
任何画面都是由点、线、面组成,其中点是重中之重,也是绘制的基础。在Compose中Canvas可组合项可以使用DSL来绘制,点同样可以使用DSL绘制。绘制点的方法也在DrawScope中定义,如下面所示:
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 // 混合模式
)
一个9个参数,有3个是必须填写的,剩下6个都有默认值,先看看这三个必要参数
-
points points是点的集合,类型为List,Offest用来表示一个坐标点,在构造方法中给其传入横纵坐标即可。
-
pointMode pointMode的意思是点的绘制方式,该参数的类型为PointMode,这个类之前没有见过,先看看它的源码:
@Suppress("INLINE_CLASS_DEPRECATED", "EXPERIMENTAL_FEATURE_WARNING")
@Immutable
inline class PointMode internal constructor(@Suppress("unused") private val value: Int) {
companion object {
/**
* 点
*/
val Points = PointMode(0)
/**
* 线
*/
val Lines = PointMode(1)
/**
* 面
*/
val Polygon = PointMode(2)
}
override fun toString() = when (this) {
Points -> "Points"
Lines -> "Lines"
Polygon -> "Polygon"
else -> "Unknown"
}
}
里面一个伴生对象,有三个属性Points、Lines和Polygon。Points表示分别绘制每个点;Lines表示将两个点的每个序列绘制为线段,如果点为奇数,则忽略最后一个点;Polygon表示将整个点序列绘制为一条线。
- color color作为必须填写的参数其实很好理解,我们需要告诉系统绘制点的颜色,否则无法进行绘制,好,看看实际场景中我们该怎么使用:
@Composable
fun CustomViewTest() {
val points = arrayListOf(
Offset(100f, 100f),
Offset(300f, 300f),
Offset(500f, 500f),
Offset(700f, 700f),
Offset(900f, 900f)
)
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
pointMode = PointMode.Points,
color = Color.Blue,
strokeWidth = 30f
)
}
}
画布大小为360dp,绘制5个点,颜色设置为蓝色,绘制宽度为30f, 效果如下图:
下面再看看绘制线段,我们用PointMode.Lines:
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
// pointMode = PointMode.Points,
pointMode = PointMode.Lines,
color = Color.Blue,
strokeWidth = 30f
)
}
我们只是改了绘制方式,可以看到将每两个点绘制为线段,因为5个点,所以最后一个点呗忽略。
最后我们把绘制方式改成PointMode.Polygon:
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
// pointMode = PointMode.Points,
// pointMode = PointMode.Lines,
pointMode = PointMode.Polygon,
color = Color.Blue,
strokeWidth = 30f
)
}
从上图可以看到,将整个点序列集合绘制成一条线。
绘制点可选的参数一共6个:strokeWidth、cap、pathEffect、alpha、colorFilter和blendmode。strokeWidth上面用过了,就是点的宽度;alpha是透明度,0~1之间的值,Float类型;colorFilter在学习Image的时候也用过,可以改变图片的颜色;剩下只有cap、pathEffect和blendmode,blendmode比较复杂,后面会学习,今天先主要看cap和pathEffect。
cap
cap参数类型是StrokeCap,用来处理线段的末端样式,默认值为StrokeCap.Butt。StrokeCap的源码如下:
@Suppress("INLINE_CLASS_DEPRECATED", "EXPERIMENTAL_FEATURE_WARNING")
@Immutable
inline class StrokeCap internal constructor(@Suppress("unused") private val value: Int) {
companion object {
val Butt = StrokeCap(0)
val Round = StrokeCap(1)
val Square = StrokeCap(2)
}
override fun toString() = when (this) {
Butt -> "Butt"
Round -> "Round"
Square -> "Square"
else -> "Unknown"
}
}
和PointMode一样,StrokeCap内也有一个伴生对象,里面有三个属性:Butt表示线段末端轮廓的起始点和结束点带有平缓的边缘,没有延伸;Round表示线段末端以半圆开始和结束的轮廓;Square表示线段末端将每个轮廓延伸笔触宽度的一半。
我们通过一个例子来看看它们三种的具体差别,我们修改上面的例子代码,strokeWidth加宽到50f,设置cap为StrokeCap.Round:
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
// pointMode = PointMode.Points,
// pointMode = PointMode.Lines,
pointMode = PointMode.Polygon,
color = Color.Blue,
strokeWidth = 50f,
cap = StrokeCap.Round
)
}
我们可以看到线段的两端变成了半圆。
现在我们把cap修改为StrokeCap.Square,看看效果:
val points = arrayListOf(
Offset(100f, 100f),
Offset(300f, 300f),
Offset(500f, 500f),
Offset(700f, 700f),
Offset(900f, 900f)
)
val points2 = arrayListOf(
Offset(900f, 100f),
Offset(700f, 300f),
Offset(500f, 500f),
Offset(300f, 700f),
Offset(100f, 900f)
)
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
// pointMode = PointMode.Points,
// pointMode = PointMode.Lines,
pointMode = PointMode.Polygon,
color = Color.Blue,
strokeWidth = 30f,
cap = StrokeCap.Butt
)
drawPoints(
points = points2,
// pointMode = PointMode.Points,
// pointMode = PointMode.Lines,
pointMode = PointMode.Polygon,
color = Color.Blue,
strokeWidth = 30f,
cap = StrokeCap.Square
)
}
左上到右下的线段cap为StrokeCap.Butt,右上到左下的cap为StrokeCap.Square,可以看出后者的线段比前者稍微长一点儿,因为Square会将线段的末端延伸笔触宽度的一半。
pathEffect
pathEffect类型是PathEffect,适用于该点的可选效果或图案,默认为null,PathEffect源码:
interface PathEffect {
companion object {
/**
* 将线段之间的锐角替换为指定半径的圆角
*/
fun cornerPathEffect(radius: Float): PathEffect = actualCornerPathEffect(radius)
/**
* 给定间距绘制一系列虚线,并将其偏移到指定间距数组中
*/
fun dashPathEffect(intervals: FloatArray, phase: Float = 0f): PathEffect =
actualDashPathEffect(intervals, phase)
/**
* 创建将内部效果应用于路径的PathEffect,然后将外部效果应用于内部效果的结果
*/
fun chainPathEffect(outer: PathEffect, inner: PathEffect): PathEffect =
actualChainPathEffect(outer, inner)
/**
* 通过指定一些特定的形状,并将其标记来绘制路径,这仅适用于笔触形状,填充形状将被忽略
*/
fun stampedPathEffect(
shape: Path,
advance: Float,
phase: Float,
style: StampedPathEffectStyle
): PathEffect = actualStampedPathEffect(shape, advance, phase, style)
}
}
可以看到PathEffect是一个接口,接口中只有一个伴生对象,有四个方法,这些方法的返回值都是PathEffect。所以想要苟安PathEffect,直接使用这四个方法即可。在实际需求选用合适的方法,我们来看看几种场景:
- 使用Brush绘制渐变 在DrawScope中绘制点其实有两个方法,上面讲到的其中一个,下面还有另外一种方法:
fun drawPoints(
points: List<Offset>,
pointMode: PointMode,
brush: Brush, // 刷子
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
)
可以看到这个绘制点的方法和上面讲过的参数基本一致,只是将上面的Color改为了这里的Brush。来猜一下,可以替换Color的参数,肯定也和颜色有关,看看Brush源码:
@Immutable
sealed class Brush {
abstract fun applyTo(size: Size, p: Paint, alpha: Float)
companion object {
/**
* 使用给定的开始坐标和结束坐标,使用提供的颜色创建线性渐变
* 颜色分散在色标对中定义的偏移处
*/
@Stable
fun linearGradient(
vararg colorStops: Pair<Float, Color>,
start: Offset = Offset.Zero,
end: Offset = Offset.Infinite,
tileMode: TileMode = TileMode.Clamp
): Brush = LinearGradient(
colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
start = start,
end = end,
tileMode = tileMode
)
/**
* 使用给定的开始坐标和结束坐标,使用提供的颜色创建线性渐变
*/
@Stable
fun linearGradient(
colors: List<Color>,
start: Offset = Offset.Zero,
end: Offset = Offset.Infinite,
tileMode: TileMode = TileMode.Clamp
): Brush = LinearGradient(
colors = colors,
stops = null,
start = start,
end = end,
tileMode = tileMode
)
/**
* 创建一个水平渐变,给定的颜色均匀分散在渐变中
*/
@Stable
fun horizontalGradient(
colors: List<Color>,
startX: Float = 0.0f,
endX: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = linearGradient(colors, Offset(startX, 0.0f), Offset(endX, 0.0f), tileMode)
/**
* 创建一个水平渐变,给定的颜色分散在色标对中定义的偏移处
*/
@Stable
fun horizontalGradient(
vararg colorStops: Pair<Float, Color>,
startX: Float = 0.0f,
endX: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = linearGradient(
*colorStops,
start = Offset(startX, 0.0f),
end = Offset(endX, 0.0f),
tileMode = tileMode
)
/**
* 创建一个垂直渐变,给定的颜色均匀地分散在渐变中
*/
@Stable
fun verticalGradient(
colors: List<Color>,
startY: Float = 0.0f,
endY: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = linearGradient(colors, Offset(0.0f, startY), Offset(0.0f, endY), tileMode)
/**
* 在Pair参数中定义的偏移处创建具有给定颜色的垂直渐变
*/
@Stable
fun verticalGradient(
vararg colorStops: Pair<Float, Color>,
startY: Float = 0f,
endY: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = linearGradient(
*colorStops,
start = Offset(0.0f, startY),
end = Offset(0.0f, endY),
tileMode = tileMode
)
/**
* 在色标对中定义的偏移处创建具有给定颜色的径向渐变
*/
@Stable
fun radialGradient(
vararg colorStops: Pair<Float, Color>,
center: Offset = Offset.Unspecified,
radius: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = RadialGradient(
colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
center = center,
radius = radius,
tileMode = tileMode
)
/**
* 创建一个径向渐变,给定的颜色均匀地分散在渐变中
*/
@Stable
fun radialGradient(
colors: List<Color>,
center: Offset = Offset.Unspecified,
radius: Float = Float.POSITIVE_INFINITY,
tileMode: TileMode = TileMode.Clamp
): Brush = RadialGradient(
colors = colors,
stops = null,
center = center,
radius = radius,
tileMode = tileMode
)
/**
* 创建给定颜色围绕中心散布的扫描渐变,并在每个色标对中定义偏移量
* 扫描从3点钟方向开始,然后顺时针继续,直到再次到达起始位置
*/
@Stable
fun sweepGradient(
vararg colorStops: Pair<Float, Color>,
center: Offset = Offset.Unspecified
): Brush = SweepGradient(
colors = List<Color>(colorStops.size) { i -> colorStops[i].second },
stops = List<Float>(colorStops.size) { i -> colorStops[i].first },
center = center
)
/**
* 创建给定颜色围绕中心散布的扫描渐变
* 扫描从3点钟方向开始,然后顺时针继续,直到再次到达起始位置
*/
@Stable
fun sweepGradient(
colors: List<Color>,
center: Offset = Offset.Unspecified
): Brush = SweepGradient(
colors = colors,
stops = null,
center = center
)
}
}
上面Brush的代码是经过删减的,可以看到删减后的代码依然很多,下面使用下Brush:
Canvas(modifier = Modifier.fillMaxSize()) {
drawPoints(
points = points,
pointMode = PointMode.Polygon,
brush = Brush.linearGradient(
colors = arrayListOf(
Color.Red,
Color.Green,
Color.Blue
)
),
strokeWidth = 30f,
)
}
代码中使用了常用的线性渐变,通过List设置了红色、绿色和蓝色,看看效果:
这样线性渐变就绘制完成了,但是颜色是均匀分布的。我们如果需要更加精准的设置每一段颜色,线性渐变也可以做到:
Canvas(modifier = Modifier.size(360.dp)) {
drawPoints(
points = points,
pointMode = PointMode.Polygon,
brush = Brush.linearGradient( // 精准渐变
0.0f to Color.Red,
0.3f to Color.Green,
0.6f to Color.Yellow,
1.0f to Color.Blue
),
strokeWidth = 30f,
)
}
我们同样使用了线性渐变,只不过设置颜色的方式和刚才不同,更加精准地设置每段的渐变颜色,看下效果:
大家在实际项目中使用渐变的时候,可以根据自己的需求实际需要,选择对应的渐变方法,好了,后面会介绍Canvas绘制线、矩形等,代码已上传:github.com/Licarey/com…