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;
}
}
答案 0 :(得分:0)
如@pskink的注释中所述,您需要使用SweepGradient
构造函数,该构造函数包含颜色列表和位置列表。 positions参数是一个浮点数数组,它指示颜色应在其中开始的360度百分比。
在我的示例中,我将画布旋转115度,然后为拱形绘制310度的后掠角。第一种颜色以0度角开始(因为画布已旋转),并且渐变以310度的角度到达第二种颜色,因此它与拱形的后掠角匹配。外环显示蓝色一直持续到360度,但蓝色始于310度。
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()
}
}