android 温度折线图绘制

匿名 (未验证) 提交于 2019-12-02 23:43:01

很多人在做天气开发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

文章来源: https://blog.csdn.net/weixin_45253393/article/details/92410225
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!