ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

android 温度折线图绘制

2019-06-16 17:50:38  阅读:312  来源: 互联网

标签:currentValue int private mPaint 折线图 android 绘制 public View


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有