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

问题描述 投票:0回答:2

我正在使用 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)
                }
            }
        }
) {

}
android android-animation android-jetpack-compose
2个回答
1
投票

有两种方法可以在 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 {
           // ...
        }
)

1
投票

虽然接受的答案是正确的,但还有更好的方法。 只需使用修饰符drawWithCache即可。对于你的情况:

Modifier
  .drawWithCache {
    // setup calculations and paints
    
    onDrawBehind {
       // draw
    }
  }

不要忘记阅读drawWithCache的文档,以确保您的代码符合重用缓存的条件(看起来确实如此)。

© www.soinside.com 2019 - 2024. All rights reserved.