何时初始化要在 Compose Canvas 中使用的资源?

When to initialize resources to be used inside Compose Canvas?

我正在使用 Jetpack Compose,我想创建一个带有自定义 shadow/gradient 效果的圆圈。据我所知,无法使用 DrawScope 内的可组合对象来创建它,我必须改用 NativeCanvas 。这对我的情况来说很好,但我记得当我们使用 View 并在 onDraw() 方法中写一些东西时,我们不应该在那里初始化新对象。由于在使用动画时每 30/60fps 调用该方法并为每次调用创建新对象将导致性能不佳。

定义这些对象 BlurMaskFilterRadialGradientPaint 的正确位置在哪里,以便仅当可组合项的大小发生变化时才可以重新初始化它们?
我想知道是否应该在函数外定义它们为 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 - 那是为什么我在 remember 调用中将 size 作为 key 传递 - 它会在 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 {
           // ...
        }
)