在圆弧上绘制圆形空心拇指 [英] Drawing a rounded hollow thumb over arc

查看:94
本文介绍了在圆弧上绘制圆形空心拇指的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个四舍五入的图形,以显示我的应用程序中的一系列值.值可分为3类:低,中,高-分别由3种颜色表示:蓝色,绿色和红色.

I want to create a rounded graph that will display a range of values from my app. The values can be classified to 3 categories: low, mid, high - that are represented by 3 colors: blue, green and red (respectively).

在此范围内,我想显示实际测量值-在相关范围部分以拇指"的形式显示:

Above this range, I want to show the actually measured values - in a form of a "thumb" over the relevant range part:

根据测量值,白拇指在范围弧上的位置可能会发生变化.

The location of the white thumb over the range arc may change, according to the measured values.

当前,我可以通过在视图的onDraw方法内部在同一中心绘制3个圆弧来绘制3色范围:

Currently, I'm able to draw the 3-colored range by drawing 3 arcs over the same center, inside the view's onDraw method:

width = (float) getWidth();
height = (float) getHeight();

float radius;

if (width > height) {
    radius = height / 3;
} else {
    radius = width / 3;
}

paint.setAntiAlias(true);
paint.setStrokeWidth(arcLineWidth);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);

center_x = width / 2;
center_y = height / 1.6f;

left = center_x - radius;
float top = center_y - radius;
right = center_x + radius;
float bottom = center_y + radius;

oval.set(left, top, right, bottom);

//blue arc
paint.setColor(colorLow);
canvas.drawArc(oval, 135, 55, false, paint);

//red arc
paint.setColor(colorHigh);
canvas.drawArc(oval, 350, 55, false, paint);

//green arc
paint.setColor(colorNormal);

canvas.drawArc(oval, 190, 160, false, paint);

这是结果弧:

我的问题是,我该怎么办:

My question is, how do I:

  1. 在这三种颜色之间创建平滑的渐变(我尝试使用 SweepGradient ,但它没有给我正确的结果).
  2. 如图所示,
  3. 创建覆盖白色拇指,以便我可以控制显示位置.

  1. Create a smooth gradient between those 3 colors (I tried using SweepGradient but it didn't give me the correct result).
  2. Create the overlay white thumb as shown in the picture, so that I'll be able to control where to display it.

动画这个白色的拇指.

Animate this white thumb over my range arc.

注意:3色范围是静态的-因此另一种解决方案可以是只将可绘制对象和白色拇指画在其上(并对其进行动画处理),所以我也乐于听到这样的解决方案:)

Note: the 3-colored range is static - so another solution can be to just take the drawable and paint the white thumb over it (and animate it), so I'm open to hear such a solution as well :)

推荐答案

对于您的前两个问题,我将使用遮罩.

I would use masks for your first two problems.

第一步是绘制两个具有线性渐变的矩形.首先矩形包含蓝色和绿色,而第二个矩形包含绿色和红色,如下图所示.我标记了两个矩形相互接触的线黑色以表明它们实际上是两个不同的矩形.

The very first step would be drawing two rectangles with a linear gradient. The first rectangle contains the colors blue and green while the second rectangle contains green and red as seen in the following picture. I marked the line where both rectangles touch each other black to clarify that they are infact two different rectangles.

这可以使用以下代码(摘录)实现:

This can be achieved using the following code (excerpt):

// Both color gradients
private Shader shader1 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(101, 172, 242), Shader.TileMode.CLAMP);
private Shader shader2 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(255, 31, 101), Shader.TileMode.CLAMP);
private Paint paint = new Paint();

// ...

@Override
protected void onDraw(Canvas canvas) {
    float width = 800;
    float height = 800;
    float radius = width / 3;

    // Arc Image

    Bitmap.Config conf = Bitmap.Config.ARGB_8888; // See other config types
    Bitmap mImage = Bitmap.createBitmap(800, 800, conf); // This creates a mutable bitmap
    Canvas imageCanvas = new Canvas(mImage);

    // Draw both rectangles
    paint.setShader(shader1);
    imageCanvas.drawRect(0, 0, 400, 800, paint);
    paint.setShader(shader2);
    imageCanvas.drawRect(400, 0, 800, 800, paint);

    // /Arc Image

    // Draw the rectangle image
    canvas.save();
    canvas.drawBitmap(mImage, 0, 0, null);
    canvas.restore();
}

由于您的目标是使用带有圆角帽的彩色圆弧,因此我们接下来需要定义这两个矩形应该对用户可见.这意味着两个矩形中的大多数将被掩盖,因此不可见.相反,唯一剩下的就是弧形区域.

As your goal is having a colored arc with rounded caps, we next need to define the area of both rectangles that should be visible to the user. This means that most of both rectangles will be masked away and thus not visible. Instead the only thing to remain is the arc area.

结果应如下所示:

为了实现所需的行为,我们定义了一个仅显示内部弧区域的遮罩矩形.为此,我们大量使用了 Paint setXfermode 方法.作为论点我们使用 PorterDuffXfermode 的不同实例.

In order to achieve the needed behavior we define a mask that only reveals the arc area within the rectangles. For this we make heavy use of the setXfermode method of Paint. As argument we use different instances of a PorterDuffXfermode.

private Paint maskPaint;
private Paint imagePaint;

// ...

// To be called within all constructors
private void init() {
    // I encourage you to research what this does in detail for a better understanding

    maskPaint = new Paint();
    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));

    imagePaint = new Paint();
    imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}

@Override
protected void onDraw(Canvas canvas) {
    // @step1

    // Mask

    Bitmap mMask = Bitmap.createBitmap(800, 800, conf);
    Canvas maskCanvas = new Canvas(mMask);

    paint.setColor(Color.WHITE);
    paint.setShader(null);
    paint.setStrokeWidth(70);
    paint.setStyle(Paint.Style.STROKE);
    paint.setStrokeCap(Paint.Cap.ROUND);
    paint.setAntiAlias(true);
    final RectF oval = new RectF();
    center_x = 400;
    center_y = 400;
    oval.set(center_x - radius,
            center_y - radius,
            center_x + radius,
            center_y + radius);

    maskCanvas.drawArc(oval, 135, 270, false, paint);

    // /Mask

    canvas.save();
    // This is new compared to step 1
    canvas.drawBitmap(mMask, 0, 0, maskPaint);
    canvas.drawBitmap(mImage, 0, 0, imagePaint); // Notice the imagePaint instead of null
    canvas.restore();
}

2.创建覆盖白色的拇指

这解决了您的第一个问题.第二个可以再次使用口罩来实现,尽管时间,我们想要取得不同的成就.以前,我们只想显示特定区域(弧形)的背景图像(两个矩形).这次我们想做相反的事情:我们定义一个背景图像(拇指)并遮盖其内部内容,以便仅中风似乎仍然存在.拇指应用到弧形图像时,将彩色弧形覆盖透明的内容区域.

2. Create the overlay white thumb

This solves your first problem. The second one can be achieved using masks again, though this time we want to achieve something different. Before, we wanted to show only a specific area (the arc) of the background image (being the two rectangles). This time we want to do the opposite: We define a background image (the thumb) and mask away its inner content, so that only the stroke seems to remain. Applied to the arc image the thumb overlays the colored arc with a transparent content area.

因此第一步将是绘制拇指.为此,我们使用与半径相同的圆弧背景弧,但是角度不同,因此弧要小得多.但是因为拇指应环绕"背景圆弧,其笔划宽度必须大于背景弧.

So the first step would be drawing the thumb. We use an arc for this with the same radius as the background arc but different angles, resulting in a much smaller arc. But becaus the thumb should "surround" the background arc, its stroke width has to be bigger than the background arc.

@Override
protected void onDraw(Canvas canvas) {
    // @step1

    // @step2

    // Thumb Image

    mImage = Bitmap.createBitmap(800, 800, conf);
    imageCanvas = new Canvas(mImage);

    paint.setColor(Color.WHITE);
    paint.setStrokeWidth(120);
    final RectF oval2 = new RectF();
    center_x = 400;
    center_y = 400;
    oval2.set(center_x - radius,
            center_y - radius,
            center_x + radius,
            center_y + radius);

    imageCanvas.drawArc(oval2, 270, 45, false, paint);

    // /Thumb Image

    canvas.save();
    canvas.drawBitmap(RotateBitmap(mImage, 90f), 0, 0, null);
    canvas.restore();
}

public static Bitmap RotateBitmap(Bitmap source, float angle)
{
    Matrix matrix = new Matrix();
    matrix.postRotate(angle);
    return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}

代码结果如下所示.

因此,现在我们的拇指覆盖了背景弧,我们需要定义遮罩去除拇指的内部,使背景弧再次可见.

So now that we have a thumb that is overlaying the background arc, we need to define the mask that removes the inner part of the thumb, so that the background arc becomes visible again.

要实现这一点,我们基本上使用与之前相同的参数来创建另一个弧,但是这次,笔划宽度必须等于用于背景弧的宽度,因为这标志着我们要在拇指内去除的区域.

To achieve this we basically use the same parameters as before to create another arc, but this time the stroke width has to be identical to the width used for the background arc as this marks the area we want to remove inside the thumb.

使用以下代码,结果图像如图4所示.

Using the following code, the resulting image is shown in picture 4.

@Override
protected void onDraw(Canvas canvas) {
    // @step1

    // @step2

    // Thumb Image
    // ...
    // /Thumb Image

    // Thumb Mask

    mMask = Bitmap.createBitmap(800, 800, conf);
    maskCanvas = new Canvas(mMask);

    paint.setColor(Color.WHITE);
    paint.setStrokeWidth(70);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    final RectF oval3 = new RectF();
    center_x = 400;
    center_y = 400;
    oval3.set(center_x - radius,
            center_y - radius,
            center_x + radius,
            center_y + radius);

    maskCanvas.drawBitmap(mImage, 0, 0, null);
    maskCanvas.drawArc(oval3, 270, 45, false, paint);

    // /Thumb Mask

    canvas.save();
    canvas.drawBitmap(RotateBitmap(mMask, 90f), 0, 0, null); // Notice mImage changed to mMask
    canvas.restore();
}

3.动画白色拇指

问题的最后一部分将为圆弧的运动设置动画.我没有固体解决方案,但也许可以为您提供有用的指导.我会尝试以下方法:

3. Animate the white thumb

The last part of your question would be animating the movement of the arc. I have no solid solution for this, but maybe can guide you in a useful direction. I would try the following:

首先将拇指定义为整个图形的一部分的 ImageView .改变时图形的选定值,将拇指图像绕背景中心旋转弧.因为我们要为运动设置动画,所以只需设置拇指图像的旋转就可以了不够.相反,我们使用 RotateAnimation 这样的方式:

First define the thumb as a ImageView that is part of your whole arc graph. When changing the selected values of your graph, you rotate the thumb image around the center of the background arc. Because we want to animate the movement, just setting the rotation of the thumb image would not be adequate. Instead we use a RotateAnimation kind of like so:

final RotateAnimation animRotate = new RotateAnimation(0.0f, -90.0f, // You have to replace these values with your calculated angles
        RotateAnimation.RELATIVE_TO_SELF, // This may be a tricky part. You probably have to change this to RELATIVE_TO_PARENT
        0.5f, // x pivot
        RotateAnimation.RELATIVE_TO_SELF,
        0.5f); // y pivot

animRotate.setDuration(1500);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);

thumbView.startAnimation(animSet);

我想这远非最终的,但这很可能会帮助您搜索所需的内容解决方案.重要的是,您的枢轴值必须参考您的中心背景圆弧,因为这是您的拇指图像应旋转的点.

This is far from final I guess, but it very well may aid you in your search for the needed solution. It is very important that your pivot values have to refer to the center of your background arc as this is the point your thumb image should rotate around.

我已经用API级别16和22、23测试了我的(完整)代码,因此我希望至少能得到这个答案为您提供了解决问题的新思路.

I have tested my (full) code with API Level 16 and 22, 23, so I hope that this answer at least gives you new ideas on how to solve your problems.

请注意, onDraw 方法中的分配操作不是一个好主意,应该被避免.为简单起见,我没有遵循此建议.该代码也将用作正确方向的指南,而不是简单地抄袭&粘贴,因为它很重使用幻数并且通常不遵循良好的编码标准.

Please note that allocation operations within the onDraw method are a bad idea and should be avoided. For simplicity I failed to follow this advise. Also the code is to be used as a guide in the right direction and not to be simply copy & pasted, because it makes heavy use of magic numbers and generally does not follow good coding standards.

这篇关于在圆弧上绘制圆形空心拇指的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆