Android - 如何绘制基于弧形的渐变?

26
我尝试创建一个弧形(可变数量的度数),使其逐渐从一种颜色过渡到另一种颜色。例如从蓝色到红色:

enter image description here
SweepGradient shader = new SweepGradient(center.x, center.y, resources.getColor(R.color.startColor),resources.getColor(R.color.endColor));
Paint paint = new Paint()
paint.setStrokeWidth(1);
paint.setStrokeCap(Paint.Cap.FILL);
paint.setStyle(Paint.Style.FILL);
paint.setShader(shader);
canvas.drawArc(rectF, startAngle, sweepAngle, true, paint);

但是结果是整个弧都用同一种颜色涂成了。

编辑:
经过更多的实验,我发现颜色的扩散是由弧的角度决定的。如果我画一个角度很小的弧,只会展示第一种颜色。角度越大,就会绘制更多的颜色。如果角度很小,似乎没有渐变效果。
这是一个例子。我画了4个弧形-90度、180度、270度和360度:

RectF rect1 = new RectF(50, 50, 150, 150);
Paint paint1 = new Paint();
paint1.setStrokeWidth(1);
paint1.setStrokeCap(Paint.Cap.SQUARE);
paint1.setStyle(Paint.Style.FILL);

SweepGradient gradient1 = new SweepGradient(100, 100,
        Color.RED, Color.BLUE);
paint1.setShader(gradient1);

canvas.drawArc(rect1, 0, 90, true, paint1);

RectF rect2 = new RectF(200, 50, 300, 150);
Paint paint2 = new Paint();
paint2.setStrokeWidth(1);
paint2.setStrokeCap(Paint.Cap.SQUARE);
paint2.setStyle(Paint.Style.FILL);

SweepGradient gradient2 = new SweepGradient(250, 100,
        Color.RED, Color.BLUE);
paint2.setShader(gradient2);

canvas.drawArc(rect2, 0, 180, true, paint2);

RectF rect3 = new RectF(50, 200, 150, 300);
Paint paint3 = new Paint();
paint3.setStrokeWidth(1);
paint3.setStrokeCap(Paint.Cap.SQUARE);
paint3.setStyle(Paint.Style.FILL);

SweepGradient gradient3 = new SweepGradient(100, 250,
        Color.RED, Color.BLUE);
paint3.setShader(gradient3);

canvas.drawArc(rect3, 0, 270, true, paint3);

RectF rect4 = new RectF(200, 200, 300, 300);
Paint paint4 = new Paint();
paint4.setStrokeWidth(1);
paint4.setStrokeCap(Paint.Cap.SQUARE);
paint4.setStyle(Paint.Style.FILL);

SweepGradient gradient4 = new SweepGradient(250, 250,
        Color.RED, Color.BLUE);
paint4.setShader(gradient4);

canvas.drawArc(rect4, 0, 360, true, paint4);

以下是结果:

在这里输入图片描述

这很令人惊讶,因为我希望红色在弧形的开头,蓝色在结尾,而中间的任何颜色都应该平均分布,无论角度如何。
我尝试使用位置参数手动排列颜色,但结果是相同的:

int[] colors = {Color.RED, Color.BLUE};
float[] positions = {0,1};
SweepGradient gradient = new SweepGradient(100, 100, colors , positions);

有什么办法可以解决这个问题吗?

4个回答

33

解决方法是将BLUE的位置设置为所需位置,可以通过以下方式实现:

int[] colors = {Color.RED, Color.BLUE};
float[] positions = {0,1};
SweepGradient gradient = new SweepGradient(100, 100, colors , positions);

这里的问题在于当将蓝色的位置设置为“1”时,并不意味着它将位于绘制弧线的末端,而是位于该弧所属圆的末端。 要解决这个问题,应考虑弧线的度数来确定蓝色的位置。因此,如果我正在绘制一条X度的弧线,则应设置位置如下:

float[] positions = {0,Xf/360f};

如果X为90,渐变将在圆形的0.25处放置蓝色:

输入图像描述


2
至少在Android 4.2上,给出的解决方案不起作用。需要在数组末尾有一个值为1的元素。像这样:int[] colors = {Color.RED, Color.BLUE, Color.BLUE}; float[] positions = {0, Xf/360f, 1}; - Tom anMoney
你知道为什么角度很小时分辨率会很低吗?比如10度的弧宽度只有3种颜色。在屏幕截图的左上角蛋糕中,你也可以看到这些步骤。 - Martin Mlostek

11

在Yoav的方案基础上进行补充。

在我的Galaxy Nexus 4.2原生Android上,给出的解决方案无法使用。

如果数组中不包含0.0f和1.0f,它似乎会被忽略。

我的最终解决方案是:

int[] colors = {Color.RED, Color.BLUE, Color.RED}; 
float[] positions = {0, Xf/360f, 1};

1
五年后,但正确的方法是将0.750f的分隔线制作成0.749f,简单的代码如下所示:
    val colors = intArrayOf(0xffff0000.toInt(), 0xff0000ff.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt())
    val positions = floatArrayOf(0.0f, 0.749f, 0.750f, 1.0f)

0

使用 SweepGradient,您可以将位置参数传递为 null(如果您想要默认渐变),并且您还可以通过 setLocalMatrix 旋转渐变。

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    val centerX = width.toFloat() / 2
    val centerY = height.toFloat() / 2

    val paint = Paint()
    paint.isAntiAlias = true

    val colors = intArrayOf(Color.RED, Color.BLUE)
    val gradient = SweepGradient(centerX, centerY, colors, null) // null position
    val matrix = Matrix()
    matrix.postRotate(270f, centerX, centerY) // rotate
    gradient.setLocalMatrix(matrix)
    paint.shader = gradient

    val radius = 400f
    val oval = RectF(centerX - radius,centerY - radius,centerX + radius,centerY + radius)
    canvas?.drawArc(oval, 0f, 360f, false, paint)
}

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