我刚刚解决了同样的问题,尽管使用RadialGradient
.
如果您想在每次绘制调用时更新着色器的位置数据,您应该预先分配着色器(如 linter 提示的那样),您还应该预先分配一个Matrix
. 着色器实例公开的唯一功能是getLocalMatrix
and setLocalMatrix
。
为了在这里做你想做的事,你将使用setLocalMatrix
,传入一个Matrix
你已经执行了一些适当转换的实例。在你的情况下,我认为一个简单的翻译转换就可以了。
正如我之前提到的,您需要预先分配一个 aMatrix
并且您将在每个绘制循环上修改这个预先分配的一个,然后再将它传递给shader.setLocalMatrix
.
这是我如何更新着色器的centerX
andcenterY
的示例。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
)
}
}
我希望这可以帮助您或将来遇到相同问题的其他人!