我想创建一个圆角图,它会显示我的应用中的一系列值。这些值可分为3类:低,中,高 - 由3种颜色表示:蓝色,绿色和红色(分别)。
在这个范围之上,我想以一个"拇指"的形式显示实际测量值。超过相关范围部分:
根据测量值,白色拇指在范围弧上的位置可能会发生变化。
目前,我可以通过在视图的onDraw方法内的相同中心绘制3个弧来绘制3色范围:
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);
这就是结果弧:
我的问题是,我该如何:
SweepGradient
但它没有给我正确的结果)。 如图所示创建叠加白拇指,以便我能够控制显示位置。
动画这个白色拇指在我的范围弧上。
注意:3色范围是静态的 - 所以另一个解决方案就是拿出可绘制的并在其上面涂上白色拇指(并为其设置动画),所以我也可以听到这样的解决方案:)
答案 0 :(得分:28)
我会为你的前两个问题使用面具。
第一步是绘制两个具有线性渐变的矩形。首先 矩形包含蓝色和绿色,而第二个矩形包含绿色 和红色如下图所示。我标记了两个矩形相互接触的线 黑色以澄清它们实际上是两个不同的矩形。
这可以使用以下代码(摘录)来实现:
// 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();
}
由于您的目标是使用带圆角的彩色圆弧,我们接下来需要定义区域 两个应该对用户可见的矩形。这意味着两个矩形的大部分 将被掩盖,因此不可见。相反,唯一要留下的是弧区。
结果应如下所示:
为了实现所需的行为,我们定义了一个只显示其中弧区域的蒙版
矩形。为此,我们大量使用setXfermode
Paint
方法。作为论点
我们使用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();
}
这解决了您的第一个问题。尽管如此,第二个可以再次使用掩模来实现 我们想要实现不同的时间。以前,我们只想显示一个特定的区域(弧形) 背景图像(是两个矩形)。这次我们想做相反的事情: 我们定义一个背景图像(拇指)并屏蔽其内部内容,仅限于此 中风似乎仍然存在。应用于弧形图像时,拇指覆盖着彩色弧线 透明的内容区域。
所以第一步就是画拇指。我们使用弧线,半径相同 背景弧但不同的角度,导致更小的弧度。但是因为 拇指应“环绕”背景弧,其笔划宽度必须大于 背景弧。
@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);
}
代码的结果如下所示。
现在我们有一个覆盖背景弧的拇指,我们需要定义蒙版 删除拇指的内部部分,以便背景弧再次可见。
为了实现这一点,我们基本上使用与之前相同的参数来创建另一个弧,但是 这次笔划宽度必须与用于背景弧的宽度相同 这标志着我们想要在拇指内移除的区域。
使用以下代码,生成的图像如图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();
}
你问题的最后一部分是动画弧的运动。我没有坚实的 解决方案,但也许可以指导你一个有用的方向。我会尝试以下方法:
首先将拇指定义为ImageView
,它是整个弧形图的一部分。改变时
选定的图形值,您可以围绕背景中心旋转拇指图像
弧。因为我们想要为运动设置动画,所以只需设置拇指图像的旋转即可
不够。相反,我们使用RotateAnimation
类似的方式:
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);
这绝不是最终的猜测,但它可以帮助您搜索所需的内容 解。您的枢轴值必须引用您的中心非常重要 背景弧,因为这是拇指图像应该旋转的点。
我已经使用API级别16和22,23测试了我的(完整)代码,所以我希望至少这个答案 为您提供有关如何解决问题的新想法。
请注意onDraw
方法中的分配操作是个坏主意
要避免。为简单起见,我没有遵循这个建议。该代码也将用作
一个正确方向的指南,而不是简单的复制和粘贴,因为它很重
使用幻数并且通常不遵循良好的编码标准。
答案 1 :(得分:1)
我会改变你绘制视图的方式,通过查看原始设计,而不是绘制3个大写,我只绘制1行,这样SweepGradient
就可以了。< / p>
这种迁移有点棘手,你有两个选择:
Path
Paint.Style.STROKE
)而另一个在它之上使它填充透明,你可以用PorterDuff xfermode实现它,它可能需要你有几次尝试,直到你得到它而不清除绿色圆圈。我想你想要为拇指位置设置动画,所以只需使用使视图无效的简单Animation
并相应地绘制拇指视图位置。
希望这有帮助
答案 2 :(得分:1)
创建渐变比跟随路径并不是那么简单。 因此,我建议您使用一些已经没有的库。
包括库:
dependencies {
...
compile 'com.github.paroca72:sc-gauges:3.0.7'
}
以XML创建量规:
<com.sccomponents.gauges.library.ScArcGauge
android:id="@+id/gauge"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
您的代码:
ScArcGauge gauge = this.findViewById(R.id.gauge);
gauge.setAngleSweep(270);
gauge.setAngleStart(135);
gauge.setHighValue(90);
int lineWidth = 50;
ScCopier baseLine = gauge.getBase();
baseLine.setWidths(lineWidth);
baseLine.setColors(Color.parseColor("#dddddd"));
baseLine.getPainter().setStrokeCap(Paint.Cap.ROUND);
ScCopier progressLine = gauge.getProgress();
progressLine.setWidths(lineWidth);
progressLine.setColors(
Color.parseColor("#65AAF2"),
Color.parseColor("#3EF2AD"),
Color.parseColor("#FF2465")
);
progressLine.getPainter().setStrokeCap(Paint.Cap.ROUND);
您的结果:
您可以在此站点上找到更复杂的内容: ScComponents