6

我一直在更新应用程序中的一些旧代码,该应用程序在 onDraw 中有很多重新分配,这些代码抱怨消息:

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

所以我已经正确更新了所有内容,没有更多警告,除了一个。线性渐变。似乎没有在对象实例上设置值的方法。而且属性不是公开的,所以你不能做 linLayout.x = value;

这是我的代码,它抱怨上面描述的警告(下划线 LinearGradient):

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

1 回答 1

1

我刚刚解决了同样的问题,尽管使用RadialGradient.

如果您想在每次绘制调用时更新着色器的位置数据,您应该预先分配着色器(如 linter 提示的那样),您还应该预先分配一个Matrix. 着色器实例公开的唯一功能是getLocalMatrixand setLocalMatrix

为了在这里做你想做的事,你将使用setLocalMatrix,传入一个Matrix你已经执行了一些适当转换的实例。在你的情况下,我认为一个简单的翻译转换就可以了。

正如我之前提到的,您需要预先分配一个 aMatrix并且您将在每个绘制循环上修改这个预先分配的一个,然后再将它传递给shader.setLocalMatrix.

这是我如何更新着色器的centerXandcenterY的示例。RadialGradient这个用例是用户可以拖动一个圆圈,我需要保持径向渐变以圆圈为中心。

下面的代码向您展示了作为自定义视图的完整工作解决方案,您可以将其复制并粘贴到您的代码库中,并在某些 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
        )
    }
}

我希望这可以帮助您或将来遇到相同问题的其他人!

于 2021-12-23T10:18:43.010 回答