我正在使用 Jetpack Compose,我想创建一个具有自定义阴影/渐变效果的圆圈。据我所知,无法使用
DrawScope
内的可组合对象创建它,我必须使用 NativeCanvas
来代替。这对于我的情况来说效果很好,但我记得当我们使用 View 并在 onDraw()
方法中编写一些内容时,我们不应该在那里初始化新对象。由于使用动画时每个 30/60fps 都会调用该方法,因此为每次调用创建新对象将导致性能不佳。
定义这些对象
BlurMaskFilter
、RadialGradient
、Paint
的正确位置在哪里,以便仅当可组合项的大小发生变化时才能重新初始化它们?lateinit var
然后使用 SideEffect
来初始化它们?
我忘了提及我正在使用 InfiniteTransition
,然后使用状态来更改在 NativeCanvas
内绘制的形状!
Box(
modifier = Modifier
.size(widthDp, widthDp)
.drawBehind {
drawIntoCanvas { canvas ->
canvas.nativeCanvas.apply {
val blurMask = BlurMaskFilter(
15f,
BlurMaskFilter.Blur.NORMAL
)
val radialGradient = android.graphics.RadialGradient(
100f, 100f, 50f,
intArrayOf(android.graphics.Color.WHITE, android.graphics.Color.BLACK),
floatArrayOf(0f, 0.9f), android.graphics.Shader.TileMode.CLAMP
)
val paint = Paint().asFrameworkPaint().apply {
shader = radialGradient
maskFilter = blurMask
color = android.graphics.Color.WHITE
}
drawCircle(100f, 100f, 50f, paint)
}
}
}
) {
}
有两种方法可以在 Compose 中的重组之间保留某些对象 - 使用
remember
或表示模型。对于这种特殊情况,remember
更适合。
如果您有
Modifier.size(widthDp, widthDp)
给出的静态尺寸,则很容易提前计算所有内容:
val density = LocalDensity.current
val paint = remember(widthDp) {
// in case you need to use width in your calculations
val widthPx = with(density) {
widthDp.toPx()
}
val blurMask = BlurMaskFilter(
15f,
BlurMaskFilter.Blur.NORMAL
)
val radialGradient = android.graphics.RadialGradient(
100f, 100f, 50f,
intArrayOf(android.graphics.Color.WHITE, android.graphics.Color.BLACK),
floatArrayOf(0f, 0.9f), android.graphics.Shader.TileMode.CLAMP
)
Paint().asFrameworkPaint().apply {
shader = radialGradient
maskFilter = blurMask
color = android.graphics.Color.WHITE
}
}
如果您没有静态尺寸,例如您想使用
Modifier.fillMaxSize
,您可以使用 Modifier.onSizeChanged
来获取实际尺寸并更新您的 Paint
- 这就是为什么我将 size
传递为 key
在 remember
调用中 - 当 key
发生变化时,它将重新计算该值。
val (size, updateSize) = remember { mutableStateOf<IntSize?>(null) }
val paint = remember(size) {
if (size == null) {
Paint()
} else {
Paint().apply {
// your code
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.onSizeChanged(updateSize)
.drawBehind {
// ...
}
)
虽然接受的答案是正确的,但还有更好的方法。 只需使用修饰符drawWithCache即可。对于你的情况:
Modifier
.drawWithCache {
// setup calculations and paints
onDrawBehind {
// draw
}
}
不要忘记阅读drawWithCache的文档,以确保您的代码符合重用缓存的条件(看起来确实如此)。