一、概述
最近看到一个要模仿华为手机天气预报的界面,于是自己试着写了一下,只做了一个简单的模仿,实现了显示设定的温度,并随着设定温度的不同,用不同颜色绘制温度区间,先看下大致思路:
- 绘制表示温度的圆弧
- 绘制显示温度文字
- 绘制刻度线
- 绘制刻度值
二、绘制
1.画外侧圆弧
代码很简单,直接看代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint_circle.setColor(Color.WHITE);
String text = String.valueOf(temperature) + "°";
int width = getWidth();
int height = getHeight();
int radiu = (width - getPaddingLeft() - getPaddingRight()) / 2 - 100;
canvas.translate(width / 2, height / 2);
RectF rectF = new RectF(-radiu, -radiu, radiu, radiu);
canvas.drawArc(rectF, 120, 300, false, paint_circle);
mPaint.setTextSize(100);
mPaint.getTextBounds(text, 0, text.length(), rect);
canvas.drawText(text, -rect.width() / 2, rect.height() / 2, mPaint);
}
前几句都是获取View的尺寸,具体的测量在onMeasure中设定,支持wrap-content和Padding,相信大家都写过这里不再粘贴代码,看看绘制圆弧的三行代码:
- canvas.translate(width / 2, height / 2); 将画布的坐标移至屏幕的中央;
- RectF rectF = new RectF(-radiu, -radiu, radiu, radiu); 设定圆弧的范围
canvas.drawArc(rectF, 120, 300, false, paint_circle); 传入起始角度和所话圆弧的角度,绘制圆弧;
2. 绘制中间的文字
mPaint.setTextSize(100);
mPaint.getTextBounds(text, 0, text.length(), rect);
canvas.drawText(text, -rect.width() / 2, rect.height() / 2, mPaint);
和一般绘制文字一样,不过要注意此时画布的原点在屏幕的中间
3. 绘制刻度线
刻度线的绘制采用不断的旋转画布,当画布旋转至水平位置时画刻度线,关键在于弄清旋转的方向和旋转角度;
private void drawItem(Canvas canvas, int radius) {
mPaint.setTextSize(10);
mPaint.setStrokeWidth(5);
int weather = -20;
canvas.save();
int number = 300 / 5;
canvas.rotate(-60);
for (int i = 0; i <= number; i++) {
if (weather <= temperature)
paint_circle.setColor(getPaintColor(weather));
else
paint_circle.setColor(Color.WHITE);
if (i == 0 || i == number) {
paint_circle.setStrokeWidth(5);
canvas.drawLine(-radius, 0, -radius + 40, 0, paint_circle);
} else {
paint_circle.setStrokeWidth(3);
canvas.drawLine(-radius, 0, -radius + 30, 0, paint_circle);
}
weather++;
canvas.rotate(5);
}
canvas.restore();
}
看看代码,我设定温度区间在-20~40度,基本上天气都在这个范围之内,当然极端天气除外,从前面的代码可以看出,圆弧的旋转角度为300度
- number;计算每个刻度线之间的角度间隔
- canvas.rotate(-60); 将canvas逆时针旋转至水平位置,作为画刻度选的位置,每条刻度线都应先旋转到此位置,再画刻度线
- getPaintColor(weather) ; 根据温度度数选择对应颜色表示温度的指数,下载的代码中有,此处就不粘贴了,
- canvas.drawLine(-radius, 0, -radius + 40, 0, paint_circle); 绘制刻度线,可以看出每个刻度线都是绘制水平线,那为什么显示之后会是不同角度的线段呢,主要就是canvas的旋转起到的作用, canvas.save();和canvas.restore();之间进行旋转绘制,之后画布在恢复原状。
4. 绘制刻度值
这一部分就是根据温度区间和圆弧的度数确定,每一个温度值对应的位置,并写上相应的数字,具体利用度数的正弦函数确定其实坐标,别忘了将度数转化为弧度制:
private void drawText(Canvas canvas, int radius) {
int textDegree = 120;
int weather = -20;
for (int i = 0; i <= 12; i++) {
String string = String.valueOf(weather) + "°";
mPaint.setTextSize(40);
mPaint.getTextBounds(string, 0, string.length(), rect1);
float x = (float) getX(radius + 20, textDegree * Math.PI / 180);
float y = (float) getY(radius + 20, textDegree * Math.PI / 180);
canvas.drawText(string, x, y - rect1.height() / 2, mPaint);
weather += 5;
textDegree += 25;
}
}
上述方法中主要的就是getX()和getY()方法,下面看看这两个方法:
private double getX(int radius, double degree) {
return getTextX(radius, degree);
}
private double getY(int radius, double degree) {
return getTextY(radius, degree);
}
private double getTextX(int radius, double degree) {
double x = 0;
double d = Math.PI;
x = radius * Math.cos(degree);
if (degree > d / 2 && degree < 3 * d / 2)
x = x - rect1.width();
return x;
}
private double getTextY(int radius, double degree) {
double y = radius * Math.sin(degree);
double d = Math.PI;
if (degree < d || degree > 2d)
y += rect1.height();
return y;
}
由于很久没用这个正弦函数,都忘记把度数转换为弧度了,之后发现每个坐标都不对,后来又看了一下那两条妖娆的正弦和余弦曲线,getTextX()和getTextY()主要是讲获取到的位置,根据实际情况调整一下,相信大家也会想到,我们获取的坐标只是对应圆弧的坐标,并不能直接在此处写上文字,因为当X坐标为负时,如果文字的位置不便宜,字就要写到圆弧里了,到此View就绘制完成了。
三、代码调用
因为View里面设置了setTemperature()的方法,此处可以使用属性动画让温度值不断增加,从而自动绘制:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_weather_view_);
westherView = (WestherView) findViewById(R.id.weather);
ObjectAnimator.ofFloat(westherView,"temperature",0,30).setDuration(2000).start();
}