I am attempting to draw a arc that is filled by a gradient
The image below is what I want
The image below is what I have now
As you can see in the images, my gradient starts too earlier
I know why this happening
If I complete the arc to form a circle I get this
As we can see, the gradient starts from 90 degrees. But my arc is drawn from 135 degrees and sweeps to 270
My question is how can get the gradient to start from 135 degrees and sweep to 270? Is it possible
This is my method so far of doing the sweep gradient
public void setProgressColourAsGradient(boolean invalidateNow) {
SweepGradient sweepGradient = new SweepGradient(baseArcRect.centerX(), baseArcRect.centerY(),progressGradientColourStart,progressGradientColourEnd);
//Make the gradient start from 90 degrees
Matrix matrix = new Matrix();
matrix.setRotate(90,baseArcRect.centerX(), baseArcRect.centerY());
sweepGradient.setLocalMatrix(matrix);
progressFillPaint.setShader(sweepGradient);
if (invalidateNow) {
invalidate();
}
}
I cannot find any API to tell SweepGradient where to actually start.
I have added all code to the appendix section
Thanks for reading!
Comment 1
I tried setting the rotation to happen at 135 degrees
Appendix A ArcWithGradient View
public class ArcWithGradient extends View {
private Paint progressFillPaint;
private RectF baseArcRect;
private int progressGradientColourStart;
private int progressGradientColourEnd;
/**
* Thickness of the arc
*/
private int thickness;
public ArcWithGradient(Context context) {
super(context);
init();
}
public ArcWithGradient(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ArcWithGradient(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
progressGradientColourStart = ContextCompat.getColor(getContext(), R.color.pinnacle_gradient_start);
progressGradientColourEnd = ContextCompat.getColor(getContext(), R.color.pinnacle_gradient_end);
thickness = UiUtils.dpToPx(getContext(), 25);
//We do not want a colour for this because we will set a gradient
progressFillPaint = CanvasUtil.makeStrokePaint(UiUtils.dpToPx(getContext(), 25), -1);
baseArcRect = new RectF(0, 0, 0, 0);
setProgressColourAsGradient(false);
}
@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {
super.onSizeChanged(width, height, oldw, oldh);
//Ensures arc is within the rectangle
float radius = Math.min(width, height) / 2;//
//I do radius - thickness so that the arc is within the rectangle
float baseArcLeft = ((width / 2) - (radius - thickness));
float baseArcTop = ((height / 2) - (radius - thickness));
float baseArcRight = ((width / 2) + (radius - thickness));
float baseArcBottom = ((height / 2) + (radius - thickness));
baseArcRect.set(baseArcLeft, baseArcTop, baseArcRight, baseArcBottom);
//Recalculate the gradient
setProgressColourAsGradient(false);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawArc(baseArcRect, 135, 270, false, progressFillPaint);
}
public void setProgressColourAsGradient(boolean invalidateNow) {
SweepGradient sweepGradient = new SweepGradient(baseArcRect.centerX(), baseArcRect.centerY(),progressGradientColourStart,progressGradientColourEnd);
//Make the gradient start from 90 degrees
Matrix matrix = new Matrix();
matrix.setRotate(90,baseArcRect.centerX(), baseArcRect.centerY());
sweepGradient.setLocalMatrix(matrix);
progressFillPaint.setShader(sweepGradient);
if (invalidateNow) {
invalidate();
}
}
}
Appendix B UiUtils
public class UiUtils {
public static int dpToPx(Context ctx, float dp) {
return Math.round(dp * ctx.getResources().getDisplayMetrics().density);
}
}
Appendix C CanvasUtil
public class CanvasUtil {
public static Paint makeStrokePaint(int width, int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.SQUARE);
paint.setStrokeWidth(width);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(color);
return paint;
}
public static Paint makeFillPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(color);
return paint;
}
}
As mentioned in the comments by @pskink you need to use the SweepGradient
constructor that takes a list of colors and a list of positions. The positions argument is an array of floats that indicate the percentage of 360 degrees where the color should begin.
In my example I'm rotating the canvas 115 degrees and then drawing a sweep angle of 310 degrees for my arch. The first color begins at an angle of 0 (because the canvas has been rotated), and the gradient reaches the second color at an angle of 310 degrees so it matches the sweep angle of the arch. The outer ring shows that the blue color continues all the way to 360 degrees, but the blue color begins at 310 degrees.
class SweepGradientView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
): View(context, attrs, defStyleAttr) {
private val archPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
strokeWidth = 32 * context.resources.displayMetrics.density
}
private val archBounds = RectF()
private val archInset = 72 * context.resources.displayMetrics.density
private val gradientColors = intArrayOf(Color.MAGENTA, Color.BLUE)
private val gradientPositions = floatArrayOf(0/360f, 310/360f)
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
val size = Math.min(width, height)
setMeasuredDimension(size, size)
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
archPaint.shader = SweepGradient(width / 2f, height / 2f, gradientColors, gradientPositions)
archBounds.set(archInset, archInset, width.toFloat() - archInset, height.toFloat() - archInset)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
canvas.save()
canvas.rotate(115f, cx, cy)
canvas.drawArc(archBounds, 0f, 310f, false, archPaint)
canvas.drawCircle(cx, cy, width / 2.2f, archPaint)
canvas.restore()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With