android 何时初始化要在合成画布内使用的资源?

qvk1mo1f  于 2023-02-06  发布在  Android
关注(0)|答案(2)|浏览(102)

我正在使用Jetpack Compose,我想创建一个带有自定义阴影/渐变效果的圆。据我所知,没有办法在DrawScope中使用可组合对象创建圆,我必须使用NativeCanvas。这对我的情况来说很好,但我记得当我们使用View并在onDraw()方法中编写一些内容时,我们不应该在那里初始化新的对象。2因为在使用动画时,该方法每30/60 fps被调用一次,并且每次调用都创建新的对象会导致性能下降。
在哪里定义这些对象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)
                }
            }
        }
) {

}
tvmytwxo

tvmytwxo1#

在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 {
           // ...
        }
)
bjp0bcyl

bjp0bcyl2#

虽然公认的答案是正确的,但还有一个更好的方法。只需使用修饰符drawWithCache。在您的情况下:

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

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

相关问题