android 温度折线图绘制
作者:互联网
很多人在做天气开发app的时候经常需要做到温度折线图
简单地对自定义的View做个详解
首先一个要弄懂使用canvas画什么,一个温度折现图其实就“画”3样东西,温度点,数值,折线段。另外一个很重要的注意点就是位置,处理不好的话很容易出现折线图不在视图中。
首先复写onMeasure方法
google已经封装好了,直接调用即可,有兴趣可以去看看resolveSize的源码,
setMeasuredDimension(resolveSize(mDefaultWidth,widthMeasureSpec),resolveSize(mDefaultHeight,heightMeasureSpec));
另外,在layout中使用的最好把layout_width和layout_height两个参数设置为wrap_parent
每个温度点对应的都是一个View,如图:
pointX设置为View宽度的一半,即:pointX = viewWidth / 2;
pointY比较难了,因为要考虑到一组数据最大值最小值有可能相差很大,数值与数值之间的差距大小问题,这里我这样处理:
首先我们知道这个View的宽高都是wrap_parent,它要配合RecyclerView使用,这个RecyclerView在layout中的高可以设置为270dp,然后添加许多自定义的View到适配器中,
自定义View的高度设为220dp:
令pointTopY<pointY<pointBottomY,那么就可以让温度点落在这个区间内。
现在有一组数据:24、18、22、19、23、24、26、28
其实算法很简单,一组数据取平均值(这里是23)让这个平均值位于中间的位置,即(pointTopY+pointBottomY) / 2 的位置,然后最大值在最上方,最小值在最下方,其他数值位于他们之间。之所以使用(pointBottomY-pointTopY) * 1f / (maxValue - minValue)去乘是因为:如果最大值最小值差别特别大,那么点与点之间的pointY的差别就比较小,如果最大值最小值差别不大,那么点与点之间的pointT的差别就大些。
接下来是绘制折线,绘制一条线段只要弄懂它的初始坐标即可,如图:
其实我们只需要绘制
点1→a1
a1→点2
点2→a2
…
a4→点5
这些线段,整个折线其实就绘制出来了,a1的坐标怎么知道呢?很容易可以看出,它的x坐标是viewWidth,它的y坐标是点1与点2坐标相加除2。然后第一个View与最后一个View分别绘制右线段和左线段,中间夹着的View绘制左右线段就完成了。
总结来说,第一个View绘制右线段,最后一个View绘制左线段,之间的所有View绘制左右线段,所有线段组合起来就是折线了。
最后贴上所有代码:
public class TemperatureView extends View {
private static final String TAG = “TemperatureView”;
private int minValue;
private int maxValue;
private int currentValue;
private int lastValue;
private int nextValue;
private Paint mPaint;
private int viewHeight;
private int viewWidth;
private int pointX;
private int pointY;
private boolean isDrawLeftLine;
private boolean isDrawRightLine;
private int pointTopY = (int) (40 * Util.getDensity(getContext()));
private int pointBottomY = (int) (200 * Util.getDensity(getContext()));
private int mMiddleValue;
public TemperatureView(Context context) {
super(context);
}
public TemperatureView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TemperatureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//设置最小值
public void setMinValue(int minValue){
this.minValue = minValue;
}
//设置最大值
public void setMaxValue(int maxValue){
this.maxValue = maxValue;
}
//设置目前的值
public void setCurrentValue(int currentValue){
this.currentValue = currentValue;
}
//设置是否画左边线段(只有第一个View是false)
public void setDrawLeftLine(boolean isDrawLeftLine){
this.isDrawLeftLine = isDrawLeftLine;
}
//设置是否画右边线段(只有最后一个View是false)
public void setDrawRightLine(boolean isDrawRightLine){
this.isDrawRightLine = isDrawRightLine;
}
//设置之前温度点的值
public void setLastValue(int lastValue){
this.lastValue = lastValue;
}
//设置下一个温度点的值
public void setNextValue(int nextValue){
this.nextValue = nextValue;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//给一个初始长、宽
int mDefaultWidth = 200;
int mDefaultHeight = (int) (220 * Util.getDensity(getContext()));
setMeasuredDimension(resolveSize(mDefaultWidth,widthMeasureSpec),resolveSize(mDefaultHeight,heightMeasureSpec));
viewHeight = getMeasuredHeight();
viewWidth = getMeasuredWidth();
pointX = viewWidth / 2;
Log.d(TAG, "onMeasure: " + viewWidth);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
mMiddleValue = (pointTopY + pointBottomY) / 2;
pointY = mMiddleValue + (int) ((pointBottomY-pointTopY) * 1f / (maxValue - minValue) * ((maxValue + minValue) / 2 - currentValue));
Log.d(TAG, "onDraw: " + pointY);
mPaint = new Paint();
drawGraph(canvas);
drawValue(canvas);
drawPoint(canvas);
}
//绘制数值
private void drawValue(Canvas canvas){
mPaint.setTextSize(40);
setTextColor();
mPaint.setStrokeWidth(0);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(currentValue+"°",pointX , pointY - 20, mPaint);
}
//设置字体颜色
public void setTextColor(){
if(currentValue <= 10 && currentValue >= 0){
mPaint.setColor(Color.BLUE);
}else if(currentValue <= 20 && currentValue > 10){
mPaint.setColor(Color.GREEN);
}else if(currentValue <= 30 && currentValue > 20){
mPaint.setColor(0xFFFF8000);
}else if(currentValue <= 40 && currentValue > 30){
mPaint.setColor(Color.RED);
}
}
//绘制温度点
public void drawPoint(Canvas canvas){
mPaint.setColor(Color.BLUE);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(pointX, pointY, 10, mPaint);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(pointX, pointY, 5, mPaint);
}
//绘制线段(线段组成折线)
public void drawGraph(Canvas canvas){
mPaint.setColor(0xFF24C3F1);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setStrokeWidth(3);
mPaint.setAntiAlias(true); //设置抗锯齿
//判断是否画左线段(第一个View不用,其他全要)
if(isDrawLeftLine){
float middleValue = currentValue - (currentValue - lastValue) / 2f;
float middleY = mMiddleValue + (int) ((pointBottomY-pointTopY) * 1f / (maxValue - minValue) * ((maxValue + minValue) / 2 - middleValue));
canvas.drawLine(0, middleY, pointX, pointY, mPaint);
}
//判断是否画右线段(最后View不用,其他全要)
if(isDrawRightLine){
float middleValue = currentValue - (currentValue - nextValue) / 2f;
float middleY = mMiddleValue + (int) ((pointBottomY-pointTopY) * 1f / (maxValue - minValue) * ((maxValue + minValue) / 2 - middleValue));
canvas.drawLine(pointX, pointY, viewWidth, middleY, mPaint);
}
}
}
最后把demo链接贴出来:https://github.com/lyx19970504/TemperatureView
标签:currentValue,int,private,mPaint,折线图,android,绘制,public,View 来源: https://blog.csdn.net/weixin_45253393/article/details/92410225