其他分享
首页 > 其他分享> > android 温度折线图绘制

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