Android - How to draw an arc based gradient

2019-01-13 11:10发布

I am trying to create an arc (variable number of degrees) that gradually goes from one color to another. From example from blue to red:

enter image description here

This is my code:

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);

But the result is the entire arc is painted with the same color.

Edit:
After more experimenting, I found out that the color spread is determined by the angle of the arc. If I draw an arc with a small angle, only the first color is displayed. The larger the angle, more colors are drawn. If the angle is small it seems that there is no gradient.
Here is an example. I am drawing 4 arcs - 90, 180, 270 and 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);

And here is the result:

enter image description here

This is surprising because I'd expect the RED to be at the start of the arc, the BLUE at the end and enything between to be spread evenly regardless of angle.
I tried to space the colors manually using the positions parameter but the results were the same:

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

Any idea how to solve this?

3条回答
一纸荒年 Trace。
2楼-- · 2019-01-13 11:22

To add to Yoav's solution.

On my Galaxy Nexus 4.2 stock Android, the solution given doesn't work.

If the array doesn't contain a 0.0f and 1.0f, it appears to be ignored.

My final solution was:

int[] colors = {Color.RED, Color.BLUE, Color.RED}; 
float[] positions = {0, Xf/360f, 1};
查看更多
迷人小祖宗
3楼-- · 2019-01-13 11:36

The solution for this is to set the position of BLUE. This is done like so:

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

The problem here is that when setting the position of BLUE to '1' this does not mean that it will be positioned at the end of the drawn arc, but instead at the end of the circle of which the arc is part of. To solve this, BLUE position should take into account the number of degrees in the arc. So if I'm drawing an arc with X degrees, position will be set like so:

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

So if X is 90, the gradient will place BLUE at 0.25 of the circle:

enter image description here

查看更多
仙女界的扛把子
4楼-- · 2019-01-13 11:36

five years later, but the right way is make 0.749f with 0.750f of the separate line, simple code is like:

    val colors = intArrayOf(0xffff0000.toInt(), 0xff0000ff.toInt(), 0xffff0000.toInt(), 0xffff0000.toInt())
    val positions = floatArrayOf(0.0f, 0.749f, 0.750f, 1.0f)
查看更多
登录 后发表回答