Android: Canvas Arc, Can Sweep Gradient Start Angle Be Changed?

时间:2017-06-12 16:43:04

标签: java android android-canvas

I am attempting to draw a arc that is filled by a gradient

The image below is what I want

enter image description here

The image below is what I have now

enter image description here

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

enter image description here

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

enter image description here

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

}

1 个答案:

答案 0 :(得分:0)

如@pskink的注释中所述,您需要使用SweepGradient构造函数,该构造函数包含颜色列表和位置列表。 positions参数是一个浮点数数组,它指示颜色应在其中开始的360度百分比。

SweepGradient example

在我的示例中,我将画布旋转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()
    }

}