在Android中绘制圆形内的弧形

11

我正在尝试在圆形内绘制一条弧线来表示温度,但我很难做到。在搜索中,我找到了以下解决方案:

这个我不理解scale方法是什么,而且绘制的是多个弧线,有些让我困惑

这篇文章里面给出了一个固定的大小,而我需要在XML布局中自定义视图控制大小

从这里我理解了角度的概念,但我并不知道如何确定椭圆的大小

到目前为止我已经达到了这个程度。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    int startTop = 0;
    int startLeft = 0;
    int endBottom = getHeight() / 2;
    int endRight = endBottom;// This makes an equal square.

    int centerX = getWidth() / 2;
    int centerY = getHeight() / 2;

    int upperEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(270 * Math.PI / 180));
    int upperEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(270 * Math.PI / 180));

    int bottomEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(90 * Math.PI / 180));
    int bottomEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(90 * Math.PI / 180));

    int leftEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(180 * Math.PI / 180));
    int leftEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(180 * Math.PI / 180));

    int rightEdgeX = (int) (centerX + getWidth() / 2 * Math.cos(0 * Math.PI / 180));
    int rightEdgeY = (int) (centerY + getWidth() / 2 * Math.sin(0 * Math.PI / 180));

    RectF rect = new RectF(startTop, startLeft, endRight, endBottom);
    canvas.drawCircle(centerX, centerY, getWidth() / 2, mBasePaint);


    canvas.drawCircle(centerX, centerY, getWidth() / 3, mCenterPaint); // White circle
}

更新: 我希望我的视图像一个甜甜圈饼图,中间将显示度数。

更新2:

我想要类似这样的东西:

输入图片描述


这个Stack Overflow的帖子可能会对你有所帮助。点击查看 - Imran Ali
1个回答

21

以下自定义View绘制两个弧线连接形成一个圆以及一个内部圆。

enter image description here

此外,我让它填充用于绘制弧线的矩形,并使用黄色背景进行View。活动背景为深色。(这些额外功能旨在更好地了解如何绘制圆/弧,它们有助于调试)

自定义View:

public class MySimpleView extends View{

private static final int STROKE_WIDTH = 20;
private Paint mBasePaint, mDegreesPaint, mCenterPaint, mRectPaint;
private RectF mRect;
private int centerX, centerY, radius;

public MySimpleView(Context context) {
    super(context);
    init();
}

public MySimpleView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public MySimpleView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init()
{
    mRectPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mRectPaint.setColor(ContextCompat.getColor(getContext(), R.color.magenta));
    mRectPaint.setStyle(Paint.Style.FILL);

    mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mCenterPaint.setColor(ContextCompat.getColor(getContext(), R.color.white));
    mCenterPaint.setStyle(Paint.Style.FILL);

    mBasePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mBasePaint.setStyle(Paint.Style.STROKE);
    mBasePaint.setStrokeWidth(STROKE_WIDTH);
    mBasePaint.setColor(ContextCompat.getColor(getContext(), R.color.blue));

    mDegreesPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mDegreesPaint.setStyle(Paint.Style.STROKE);
    mDegreesPaint.setStrokeWidth(STROKE_WIDTH);
    mDegreesPaint.setColor(ContextCompat.getColor(getContext(), R.color.green));

}


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    // getHeight() is not reliable, use getMeasuredHeight() on first run:
    // Note: mRect will also be null after a configuration change,
    // so in this case the new measured height and width values will be used:
    if (mRect == null)
    {
        // take the minimum of width and height here to be on he safe side:
        centerX = getMeasuredWidth()/ 2;
        centerY = getMeasuredHeight()/ 2;
        radius = Math.min(centerX,centerY);

        // mRect will define the drawing space for drawArc()
        // We have to take into account the STROKE_WIDTH with drawArc() as well as drawCircle():
        // circles as well as arcs are drawn 50% outside of the bounds defined by the radius (radius for arcs is calculated from the rectangle mRect).
        // So if mRect is too large, the lines will not fit into the View
        int startTop = STROKE_WIDTH / 2;
        int startLeft = startTop;

        int endBottom = 2 * radius - startTop;
        int endRight = endBottom;

        mRect = new RectF(startTop, startLeft, endRight, endBottom);
    }

    // just to show the rectangle bounds:
    canvas.drawRect(mRect, mRectPaint);

    // subtract half the stroke width from radius so the blue circle fits inside the View
    canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH / 2, mBasePaint);
    // Or draw arc from degree 192 to degree 90 like this ( 258 = (360 - 192) + 90:
    // canvas.drawArc(mRect, 192, 258, false, mBasePaint);

    // draw an arc from 90 degrees to 192 degrees (102 = 192 - 90)
    // Note that these degrees are not like mathematical degrees:
    // they are mirrored along the y-axis and so incremented clockwise (zero degrees is always on the right hand side of the x-axis)
    canvas.drawArc(mRect, 90, 102, false, mDegreesPaint);

    // subtract stroke width from radius so the white circle does not cover the blue circle/ arc
    canvas.drawCircle(centerX, centerY, radius - STROKE_WIDTH, mCenterPaint);
}
}

1
@MotassemJa - 目前,视图将在左上角绘制(假设为LTR区域设置)。对于上面的图片,我使用了120dp作为宽度和高度。要使"wrap_content"生效,我想必须先有一些内容。您自定义的视图是否有某种子视图? - Bö macht Blau
不,我的自定义视图是空的。如果我在里面放一个单独的View元素,它会起作用吗? - Motassem Jalal
1
@MotassemJa - 我认为dp值用于View的宽度和高度。在xml文件中使用非硬编码的dp值而是引用dimen值是一个好主意。这样你可以根据屏幕大小提供不同的dimen值。 - Bö macht Blau
@MotassemJa - 嗯,要实现这个功能,您可以在画布上绘制文本,或者考虑使用RelativeLayout,将自定义视图作为最低元素添加到RelativeLayout中,并将一些TextView重叠在自定义视图上,然后通过布局xml文件中的RelativeLayout参数分配所需的大小、文本样式和位置。这可能值得再问一个问题 - 我相信有很多布局大师比我更有经验 :) - Bö macht Blau
我想选择第一种选项,只是为了避免在每个RelativeLayout中添加6个TextView。我尝试使用canvas.drawText("Temp 1", canvas.getWidth() / 2, canvas.getHeight() + STROKE_WIDTH, mTextPaint); 添加文本,但它没有显示在视图上。 - Motassem Jalal
显示剩余4条评论

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