自定义天气显示温度变化的LinearChart控件

这次发表的是前几个月搞定的一个自定义控件,那时自己在写一个小的查看天气的软件,在这过程中就涉及了显示天气变化的折线图,一开始想用一些画图框架来解决问题,不过考虑到就只用到LineChart折线图这一个控件就要导一个库有点太浪费了,所以就自己自定义简易版LineChart算了。好了不说闲话老规矩,先发张效果图先:

img.PNG

这就是这个自定义控件的最终效果,当然颜色你可以自己设置。 首先初始化自定义控件的各个变量,以便看得更清楚:

    //圆点旁边字体的大小
    private int CircleTextSize;
    //字体颜色
    private int CircleTextColor;
    //高的温度的线的颜色
    private int MinLineColor;
    //低的温度的线的颜色
    private int MaxLineColor;
    //圆点的颜色
     private int CircleColor;
    //画线的画笔
    private Paint LinePaint;
    //画圆点的画笔
    private Paint CirclePaint;
    //画字的画笔
    private Paint TextPaint;
    //存储Max轴的数据
    private List<Float> YValueMax=new ArrayList<>();
    //存储Min轴的数据
    private List<Float> YValueMin=new ArrayList<>();
    //控件的高度
    private int ChartHeight=0;
    //控件的长度
    private int ChartWidth=0;
    //缓存X轴的数据
    private List<Float> XValueWidth=new ArrayList<>();
    //画出Y轴最大值的数据
    private List<Float> mYAxisMax=new ArrayList<>();
    //画出Y轴最小值的数据
    private List<Float> mYAxisMin=new ArrayList<>();
    //设置透明度
    private int ChartAlpha=0;
    //圆点的半径
    private float mRadius=0;
    //折线的粗细
    private float StrokeWidth=0;
    //文字和上下的边的间隔
    private float marginHeigh=0;    

接着就是初始化各个自定义的变量:

public WeatherLineChart(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //初始化各参数
        TypedArray typedArray=context.getTheme().obtainStyledAttributes(
                attrs,R.styleable.WeatherLineChart,defStyleAttr,0);
        int numCount=typedArray.getIndexCount();
        for(int i=0;i<numCount;i++){
            int attr= typedArray.getIndex(i);
            switch(attr){
                case R.styleable.WeatherLineChart_MaxLineColor:
                      MaxLineColor=typedArray.getColor(attr, Color.RED);
                    break;
                case R.styleable.WeatherLineChart_MinLineColor:
                      MinLineColor=typedArray.getColor(attr,Color.BLUE);
                    break;
                case R.styleable.WeatherLineChart_CircleTextColor:
                      CircleTextColor=typedArray.getColor(attr,Color.GRAY);
                    break;
                case R.styleable.WeatherLineChart_CircleTextSize:
                      CircleTextSize=typedArray.getDimensionPixelSize(attr,(int)TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP,15,getResources().getDisplayMetrics()));
                    break;
                case R.styleable.WeatherLineChart_CircleColor:
                      CircleColor=typedArray.getColor(attr,Color.BLACK);
                    break;
                case R.styleable.WeatherLineChart_ChartAlpha:
                      ChartAlpha=typedArray.getInt(attr,220);
                    break;
            }
        }
        typedArray.recycle();

        float density=getResources().getDisplayMetrics().density;
        mRadius = 3 * density;
        StrokeWidth=density*3;
    marginHeigh=density*10;

        display=((WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        WrapcontentWidth=display.getWidth();
        WrapcontentHight=display.getHeight();

        //初始化画线的画笔
        LinePaint=new Paint();
        LinePaint.setAntiAlias(true);
        LinePaint.setStyle(Paint.Style.STROKE);
        LinePaint.setStrokeWidth(StrokeWidth);
        LinePaint.setAlpha(ChartAlpha);

        //初始化画圆点的画笔
        CirclePaint=new Paint();
        CirclePaint.setAntiAlias(true);
        CirclePaint.setColor(CircleColor);
        CirclePaint.setAlpha(ChartAlpha);

        //初始化画字的画笔
        TextPaint=new Paint();
        TextPaint.setAntiAlias(true);
        TextPaint.setTextSize(CircleTextSize);
        TextPaint.setColor(CircleTextColor);
        TextPaint.setTextAlign(Paint.Align.CENTER);
        TextPaint.setAlpha(ChartAlpha);

    }

这的代码虽然有点多,不过都只是一些初始化的操作而已,所以看起来也不会很复杂。而最重要的代码段当然是绘制View的onDraw()方法。代码如下:

protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        ChartHeight=getHeight();
        ChartWidth=getWidth();
        if(XValueWidth!=null&&mYAxisMax!=null&&mYAxisMin!=null){
            XValueWidth.clear();
            mYAxisMax.clear();
            mYAxisMin.clear();
        }
        //初始化X轴的值
        initXValueData();
        //初始化Y轴的值
        initYValueData();
        //画出最大值的线
        DrawLine(canvas,XValueWidth,mYAxisMax,YValueMax,true);
        //画出最小值得线
        DrawLine(canvas,XValueWidth,mYAxisMin,YValueMin,false);
    }

这个onDraw()方法最重要的就是底下的四个方法。其中initXValueData()是算出各个点在这个控件的X轴的位置数据,initYValueData()是画出两条线的Y轴的位置数据。剩下的DrawLine()方法就是具体的画出每条折线。接下来,看看initXValueData()方法:

    //初始化X轴的值
    public void initXValueData(){
         //得到数据的个数
        int XNum=YValueMax.size();
        //得到距离最左边的距离
        float BaseWidth=ChartWidth/(XNum*2);
        //得到各点之间的间隔
        float tempWdith=BaseWidth*2;
        for(int i=0;i<XNum;i++){
        //得到各点的具体X轴坐标
            XValueWidth.add(BaseWidth);
            BaseWidth+=tempWdith;
        }
    }

这个方法我注释已经很清楚了,就是得到第一个点到最左边的距离(BaseWidth)。而各个点之间的距离是BaseWidth的两倍,进而就可以得到每个点的X轴的坐标数据。然后就是initYValueData(),代码如下:

    //初始化Y轴的值
    public void initYValueData(){
        //获取最大值
        float tempMax=YValueMax.get(0);
        //获取最小值
        float tempMin=YValueMax.get(0);

        //算出最高温度的最大值的最小值
        for(int i=1;i<YValueMax.size();i++){
            if(tempMax<YValueMax.get(i)){
                tempMax=YValueMax.get(i);
            }
            if(tempMin>YValueMax.get(i)){
                tempMin=YValueMax.get(i);
            }
        }

        //和最高温度的最大值和最小值比较进而得到所有数据的最大值和最小值
        for(int i=1;i<YValueMin.size();i++){
            if(tempMax<YValueMin.get(i)){
                tempMax=YValueMin.get(i);
            }
            if(tempMin>YValueMin.get(i)){
                tempMin=YValueMin.get(i);
            }
        }

      //温差
      float parts=tempMax-tempMin;
      //y轴一端到控件一端的距离
      float length = CircleTextSize+mRadius+marginHeigh;
      //y轴高度
      float yAxisHeight = ChartHeight-length*2;

        if(parts==0){
            //都为零没有温差
            for(int i=0;i<YValueMax.size();i++){
                mYAxisMax.add((float) (ChartHeight/2));
                mYAxisMin.add((float) (ChartHeight/2));
            }
        }else{
            //有温差
            float partVlaue=yAxisHeight/parts;
            //最小高度值
            for(int i=0;i<YValueMax.size();i++){
                //具体的Y轴坐标数据
                mYAxisMax.add(ChartHeight-partVlaue*(YValueMax.get(i)-tempMin)-length);
                mYAxisMin.add(ChartHeight-partVlaue*(YValueMin.get(i)-tempMin)-length);
            }
        }
    }

初始化Y轴的坐标数据时略显复杂。总的思路就是首先的得到上下两个折线总的数据的最大值和最小值。即tempMax和tampMin分别是总数据的最大值和最小值。最大值和最小值的相减即可得到温差。因为两条折线的上下是有文字显示每个点的,所以实际的Y轴的高度是整个View的高度减去文字大小和原点半径和设置的间隔。即//y轴一端到控件一端的距离 float length = CircleTextSize+mRadius+marginHeigh; //y轴高度 float yAxisHeight = ChartHeight-length*2;这段代码的意思。当温差(parts)等于0时,即各点温度都是一样的时候,两条折线是显示在整个View的中间的。否则是有温差情况,高度除于温差得到最小的高度值float partVlaue=yAxisHeight/parts;,然后整个View的高度减去每个实际的温度数据减去最小值再乘以最小的高度值的值在减去底下的文字高度等(length),就是这一点具体的Y轴的高度。上下两条的折线的原理都是一样的,为此就可以得到具体的Y轴的位置数值。 其实大部分代码都是在初始化数据,等数据初始化完之后就是画图的阶段了,代码如下:

//画图
  public void DrawLine(Canvas canvas,List<Float> XValue,List<Float> mYAxis,List<Float> YValue,boolean top){
        for(int i=0;i<XValue.size();i++){
            if(top){
                //画具体温度数据
                LinePaint.setColor(MaxLineColor);
                canvas.drawText(YValue.get(i)+"",XValue.get(i),mYAxis.get(i)-mRadius,TextPaint);
            }else{
                LinePaint.setColor(MinLineColor);
                //画具体温度数据
                canvas.drawText(YValue.get(i)+"",XValue.get(i),mYAxis.get(i)+CircleTextSize+mRadius,TextPaint);
            }
            if(i!=XValue.size()-1){
                
                //画每两点之间的连线
                canvas.drawLine(XValue.get(i),mYAxis.get(i),XValue.get(i+1),mYAxis.get(i+1),LinePaint);
            }
            //画每一点的原点
            canvas.drawCircle(XValue.get(i),mYAxis.get(i),mRadius,CirclePaint);
        }
    }

其中top参数假如是true的话代表的是上面一条折线,false的画代表的是下面的一条折线图。其实只要得到上面的各个点的X,Y轴坐标的数据之后剩下的只是用Canvas进行画线,画点和画文字,具体的看代码注释,注释已经写得很清楚了。

最后奉上源码。如果对你有帮助就请给我给星星或喜欢吧

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

扫码关注云+社区

领取腾讯云代金券