安卓线性渐变: 预分配和重复使用

7

我一直在更新一个应用程序中的一些旧代码,这些代码在onDraw中有许多重新分配,并提示以下消息:

Avoid object allocations during draw/layout operations (preallocate and reuse instead).

我已经将所有内容更新并且没有出现警告,除了一个。LinearGradient。似乎没有方法可以在对象实例上设置值。而且属性不是公共的,因此您不能执行linLayout.x = value;。

这是我的代码,它显示上述警告(在LinearGradient下划线):

myPaintGradient.setShader(new LinearGradient(deviation,6,halfwidth,LinearGradientSize,barColorGreen, barColorRed, android.graphics.Shader.TileMode.CLAMP));

嗨,Jessse,你能解决这个问题吗? - Arka Prava Basu
@ArkaPravaBasu - 请查看我下面的答案。 - Thomas Cook
1个回答

1

我刚刚解决了同样的问题,虽然使用的是RadialGradient

如果你想在每次绘制调用时更新着色器的位置数据,你应该预分配着色器(如提示所示),并且还应该预分配一个Matrix。着色器实例仅公开getLocalMatrixsetLocalMatrix功能。

要做到这一点,您将使用setLocalMatrix,传入一个Matrix实例,并对其执行一些适当的变换。在您的情况下,我认为一个简单的平移变换就可以解决问题。

正如我之前提到的,您需要预分配一个Matrix,并在每个绘制循环中修改此预分配的矩阵,然后将其传递到shader.setLocalMatrix中。

以下是我如何更新RadialGradient着色器的centerXcenterY的示例。这种用例是用户可以拖动圆形,我需要使径向渐变保持在圆形的中心。

下面的代码显示了一个完整的工作解决方案,作为自定义视图,您可以将其复制并粘贴到您的代码库中,并在某些XML中使用。

有趣的部分在于onDraw覆盖,我正在进行矩阵变换并更新着色器:

class MoveableGradientCircle @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    // Struct for all the data which needs pre-allocating
    private data class Circle(
        val radius: Float,
        val centerX: Float,
        val centerY: Float,
        val paint: Paint,
        val shaderMatrix: Matrix
    )
    // Pre-allocate the data
    private var circle: Circle =  Circle(
        radius = 10f,
        centerX = x,
        centerY = y,
        paint = Paint().apply {
            isAntiAlias = true
            style = Paint.Style.FILL_AND_STROKE
            shader = RadialGradient(
                centerX = 0f,
                centerY = 0f,
                radius = 10f,
                startColor = Color.RED,
                edgeColor = Color.GREEN,
                tileMode = Shader.TileMode.CLAMP
            )
        },
        shaderMatrix = Matrix()
    )

    // Setup a touch listener to update the circles x/y positions on user touch location
    init {
        setOnTouchListener { view, event ->
            view.performClick()
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    circle = circle.copy(
                        centerX = event.x,
                        centerY = event.y
                    )
                    invalidate()
                    true
                }
                MotionEvent.ACTION_MOVE -> {
                    circle = circle.copy(
                        centerX = event.x,
                        centerY = event.y
                    )
                    invalidate()
                    true
                }
                MotionEvent.ACTION_UP -> {
                    circle = circle.copy(
                        centerX = event.x,
                        centerY = event.y
                    )
                    invalidate()
                    true
                }
                else -> false
            }
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        // No need to keep re-allocating shader
        // Instead, we update the pre-allocated matrix
        // In this case, we'll just translate it to the circles current center x,y
        // which happens to be the users touch location
        circle.shaderMatrix.reset()
        circle.shaderMatrix.setTranslate(
            circle.centerX,
            circle.centerY
        )
        // Update the matrix on the shader
        circle.paint.shader.setLocalMatrix(circle.shaderMatrix)
        // Draw the arc with the updated paint
        canvas?.drawArc(
            circle.centerX - circle.radius,
            circle.centerY - circle.radius,
            circle.centerX + circle.radius,
            circle.centerY + circle.radius,
            0f,
            360f,
            true,
            circle.paint
        )
    }
}

我希望这能帮助你或未来遇到同样问题的人!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接