Android用Canvas画一个折线图,并加以简单封装

昨天晚上跟朋友聊天,聊到绘制折线,然后多画了几笔,画了个折线图。(老规矩后面有源码)

先看一下效果图

view宽高设置为正方形
view宽高设置为mach_parent

先来绘制一个固定的折线图

1.先画一个xy坐标轴。

   Paint daxesPaint,axispointPaint,brokenLinePaint;
        //画布宽度
        canvasWidth = canvas.getWidth();
        //画布高度
        canvasHeight = canvas.getHeight();
        widthCriterion = canvasWidth /10;    //将画布宽分为10份
        hightCriterion = canvasHeight /10;   //将画布高分为10份
        minCriterion = widthCriterion > hightCriterion ? hightCriterion /2: widthCriterion /2; //画xy轴角的依据
       
        daxesPaint=new Paint();
        daxesPaint.setColor(Color.BLACK);
        daxesPaint.setAntiAlias(true);  //去掉锯齿效果
        daxesPaint.setStrokeWidth(7.0f);//画笔宽度
        //第一个方法:画xy轴
        drawDaxes(canvas,daxesPaint);   
drawDaxes方法如下:
 private void  drawDaxes(Canvas canvas,Paint p){
       //开始y绘制坐标系
       canvas.drawLine(widthCriterion,hightCriterion,widthCriterion,hightCriterion*9,p);
       //绘制y角
       canvas.drawLine(widthCriterion-minCriterion,hightCriterion+minCriterion,widthCriterion+2,hightCriterion,p);
       canvas.drawLine(widthCriterion,hightCriterion,widthCriterion+minCriterion-2,hightCriterion+minCriterion,p);
       //开始x绘制坐标系
       canvas.drawLine(widthCriterion-4,hightCriterion*9,widthCriterion*9,hightCriterion*9,p);
       //绘制x角
       canvas.drawLine(widthCriterion*9-minCriterion,hightCriterion*9-minCriterion,widthCriterion*9,hightCriterion*9+2,p);
       canvas.drawLine(widthCriterion*9-minCriterion,hightCriterion*9+minCriterion,widthCriterion*9,hightCriterion*9-2,p);

   }

效果如下:

在这里插入图片描述
2.再绘制x,y轴坐标

 //开始绘制xy轴坐标
        axispointPaint=daxesPaint;
        drawAxispoint(canvas,axispointPaint);

drawAxispoint方法如下

 private void drawAxispoint(Canvas canvas,Paint p){
       textFont=widthCriterion/5*2;
       Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
       p.setTypeface( font );
       p.setTextSize(textFont);
       for (int i = 1; i <=8 ; i++) {
           String text= String.valueOf(-1+i);
           int stringWidth = (int) p.measureText(text);     //文本长度
           canvas.drawText(text, i*widthCriterion-stringWidth/2, hightCriterion*9+textFont, p);// 画文本
       }
       for (int i = 1; i <=7 ; i++) {
           String text= String.valueOf(i);
           int stringWidth = (int) p.measureText(text);
           //文本长度
           canvas.drawText(text, widthCriterion-textFont, hightCriterion*9-i*hightCriterion+stringWidth/2, p);// 画文本
       }
   }

效果图如下:

在这里插入图片描述
3.最后绘制折线和坐标点

 //开始绘制折线和坐标点
        brokenLinePaint=axispointPaint;
        brokenLinePaint.setStrokeWidth(5.0f);
        drawbrokenLine(canvas,brokenLinePaint);

drawbrokenLine方法如下:

 private void drawbrokenLine(Canvas canvas,Paint p){
       canvas.drawLine(widthCriterion,hightCriterion*9,widthCriterion*2,hightCriterion*2,p);
       canvas.drawLine(widthCriterion*2,hightCriterion*2,widthCriterion*3,hightCriterion*5,p);
       canvas.drawLine(widthCriterion*3,hightCriterion*5,widthCriterion*4,hightCriterion*7,p);
       canvas.drawLine(widthCriterion*4,hightCriterion*7,widthCriterion*5,hightCriterion*6,p);
       canvas.drawLine(widthCriterion*5,hightCriterion*6,widthCriterion*6,hightCriterion*7,p);
       canvas.drawLine(widthCriterion*6,hightCriterion*7,widthCriterion*7,hightCriterion*2,p);
       canvas.drawLine(widthCriterion*7,hightCriterion*2,widthCriterion*8,hightCriterion*3,p);
       //画折线上的点
       canvas.drawCircle(widthCriterion, hightCriterion*9, 10, p);
       canvas.drawCircle(widthCriterion*2,hightCriterion*2, 10, p);
       canvas.drawCircle(widthCriterion*3,hightCriterion*5, 10, p);
       canvas.drawCircle(widthCriterion*4,hightCriterion*7, 10, p);
       canvas.drawCircle(widthCriterion*5,hightCriterion*6, 10, p);
       canvas.drawCircle(widthCriterion*6,hightCriterion*7, 10, p);
       canvas.drawCircle(widthCriterion*7,hightCriterion*2, 10, p);
       canvas.drawCircle(widthCriterion*8,hightCriterion*3, 10, p);

   }

效果图如下:

在这里插入图片描述

固定的折线图java文件如下:

public class LineChartView extends View {

    private int minCriterion;
    private int hightCriterion;
    private int widthCriterion;
    private int canvasHeight;
    private int canvasWidth;
    private int textFont;


    public LineChartView(Context context) {
        super(context);
    }

    public LineChartView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawAxis(canvas);
    }

    //绘制
    private void drawAxis(Canvas canvas){
        Paint daxesPaint,axispointPaint,brokenLinePaint;
        //画布宽度
        canvasWidth = canvas.getWidth();
        //画布高度
        canvasHeight = canvas.getHeight();
        widthCriterion = canvasWidth /10;    //将画布宽分为10份
        hightCriterion = canvasHeight /10;   //将画布高分为10份
        minCriterion = widthCriterion > hightCriterion ? hightCriterion /2: widthCriterion /2; //画xy轴角的依据

        daxesPaint=new Paint();
        daxesPaint.setColor(Color.BLACK);
        daxesPaint.setAntiAlias(true);  //去掉锯齿效果
        daxesPaint.setStrokeWidth(7.0f);//画笔宽度
        //第一个方法:画xy轴
        drawDaxes(canvas,daxesPaint);

        //开始绘制xy轴坐标
        axispointPaint=daxesPaint;
        drawAxispoint(canvas,axispointPaint);

        //开始绘制折线和坐标点
        brokenLinePaint=axispointPaint;
        brokenLinePaint.setStrokeWidth(5.0f);
        drawbrokenLine(canvas,brokenLinePaint);
    }


   private void  drawDaxes(Canvas canvas,Paint p){
       //开始y绘制坐标系
       canvas.drawLine(widthCriterion,hightCriterion,widthCriterion,hightCriterion*9,p);
       //绘制y角
       canvas.drawLine(widthCriterion-minCriterion,hightCriterion+minCriterion,widthCriterion+2,hightCriterion,p);
       canvas.drawLine(widthCriterion,hightCriterion,widthCriterion+minCriterion-2,hightCriterion+minCriterion,p);
       //开始x绘制坐标系
       canvas.drawLine(widthCriterion-4,hightCriterion*9,widthCriterion*9,hightCriterion*9,p);
       //绘制x角
       canvas.drawLine(widthCriterion*9-minCriterion,hightCriterion*9-minCriterion,widthCriterion*9,hightCriterion*9+2,p);
       canvas.drawLine(widthCriterion*9-minCriterion,hightCriterion*9+minCriterion,widthCriterion*9,hightCriterion*9-2,p);

   }

   private void drawAxispoint(Canvas canvas,Paint p){
       textFont=widthCriterion/5*2;
       Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
       p.setTypeface( font );
       p.setTextSize(textFont);
       for (int i = 1; i <=8 ; i++) {
           String text= String.valueOf(-1+i);
           int stringWidth = (int) p.measureText(text);     //文本长度
           canvas.drawText(text, i*widthCriterion-stringWidth/2, hightCriterion*9+textFont, p);// 画文本
       }
       for (int i = 1; i <=7 ; i++) {
           String text= String.valueOf(i);
           int stringWidth = (int) p.measureText(text);
           //文本长度
           canvas.drawText(text, widthCriterion-textFont, hightCriterion*9-i*hightCriterion+stringWidth/2, p);// 画文本
       }
   }

   private void drawbrokenLine(Canvas canvas,Paint p){
       canvas.drawLine(widthCriterion,hightCriterion*9,widthCriterion*2,hightCriterion*2,p);
       canvas.drawLine(widthCriterion*2,hightCriterion*2,widthCriterion*3,hightCriterion*5,p);
       canvas.drawLine(widthCriterion*3,hightCriterion*5,widthCriterion*4,hightCriterion*7,p);
       canvas.drawLine(widthCriterion*4,hightCriterion*7,widthCriterion*5,hightCriterion*6,p);
       canvas.drawLine(widthCriterion*5,hightCriterion*6,widthCriterion*6,hightCriterion*7,p);
       canvas.drawLine(widthCriterion*6,hightCriterion*7,widthCriterion*7,hightCriterion*2,p);
       canvas.drawLine(widthCriterion*7,hightCriterion*2,widthCriterion*8,hightCriterion*3,p);
       //画折线上的点
       canvas.drawCircle(widthCriterion, hightCriterion*9, 10, p);
       canvas.drawCircle(widthCriterion*2,hightCriterion*2, 10, p);
       canvas.drawCircle(widthCriterion*3,hightCriterion*5, 10, p);
       canvas.drawCircle(widthCriterion*4,hightCriterion*7, 10, p);
       canvas.drawCircle(widthCriterion*5,hightCriterion*6, 10, p);
       canvas.drawCircle(widthCriterion*6,hightCriterion*7, 10, p);
       canvas.drawCircle(widthCriterion*7,hightCriterion*2, 10, p);
       canvas.drawCircle(widthCriterion*8,hightCriterion*3, 10, p);

   }


}

下面来简单封装一下

1.首先提供给外界输入数据的方法:

    public void setChartdate(String[] xdate, int[] ydate, float[] linedate) {
        this.xdate = xdate;    //x轴坐标
        this.ydate = ydate;    //y轴坐标
        this.linedate = linedate; //坐标点的y轴上的位置
    }

2.进行数据为空判断和越界判断

   if (xdate.length!=0&&ydate.length!=0&&linedate.length!=0&&xdate.length>=linedate.length){
            if (yMaxdata()>=lineMaxdata()){
                drawAxis(canvas);
            }

        }

其中yMaxdata(),lineMaxdata()方法作用为取ydate与linedate中的最大值

3.将所有写死的数据与传进来的数据产生联系。

    private void drawDaxes(Canvas canvas, Paint p) {
        //开始y绘制坐标系
        canvas.drawLine(widthCriterion, hightCriterion, widthCriterion, hightCriterion * (yCopies - 1), p);
        //绘制y角
        canvas.drawLine(widthCriterion - minCriterion, hightCriterion + minCriterion, widthCriterion + 2, hightCriterion, p);
        canvas.drawLine(widthCriterion, hightCriterion, widthCriterion + minCriterion - 2, hightCriterion + minCriterion, p);
        //开始x绘制坐标系
        canvas.drawLine(widthCriterion - 4, hightCriterion * (yCopies - 1), widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1), p);
        //绘制x角
        canvas.drawLine(widthCriterion * (xCopies - 1) - minCriterion, hightCriterion * (yCopies - 1) - minCriterion, widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1) + 2, p);
        canvas.drawLine(widthCriterion * (xCopies - 1) - minCriterion, hightCriterion * (yCopies - 1) + minCriterion, widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1) - 2, p);

    }

    private void drawAxispoint(Canvas canvas, Paint p) {
        textFont = widthCriterion / 5 * 2;
        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
        p.setTypeface(font);
        p.setTextSize(textFont);
        //画x轴数据
        for (int i = 0; i < xdate.length; i++) {
            String text = xdate[i];
            int stringWidth = (int) p.measureText(text);     //文本长度
            canvas.drawText(text, (i + 1) * widthCriterion - stringWidth / 2, hightCriterion * (yCopies - 1) + textFont, p);// 画文本
        }
        for (int i = 0; i < ydate.length; i++) {
            String text = String.valueOf(ydate[i]);

            int stringWidth = (int) p.measureText(text);
            //文本长度
            if (i == 0) {
            } else {
                canvas.drawText(text, widthCriterion - textFont-stringWidth, hightCriterion * (yCopies - 1) - i * hightCriterion + stringWidth / 2, p);// 画文本

            }
        }
    }

    private void drawbrokenLine(Canvas canvas, Paint p) {
        float line=(hightCriterion * (yCopies - 1)-hightCriterion*2)/ydate[ydate.length-1];

        for (int i = 0; i <linedate.length; i++) {
            float height=hightCriterion * (yCopies-1)-line*linedate[i];
            if (i!=linedate.length-1){
                float elseheight=hightCriterion * (yCopies-1)-line*linedate[i+1];
                canvas.drawLine(widthCriterion*(i+1),height , widthCriterion * (i+2), elseheight, p);
                canvas.drawCircle(widthCriterion*(i+1), height, 10, p);
            }else{
                float endheight=hightCriterion * (yCopies-1)-line*linedate[linedate.length-1];
                canvas.drawCircle(widthCriterion*(i+1), endheight, 10, p);
            }

        }
    }

现在就可以根据给到的数据动态绘制简单折线图

接下来看效果
在Activity中找到控件后,调用控件的setChartdate()方法;
数据如下:

    private String[] xdata={"0","1","2","3","4","5","6","7","8"};
    private int[] yfata={0,10,20,30,40,50,60,70};
    private float[] linedata={5,10,6,30,5,62.5f,6,2,};

传入数据:

    linechartview.setChartdate(xdata,yfata,linedata);

效果图如下:
在这里插入图片描述
封装后java代码如下

public class LineChartView extends View {

    private int minCriterion;
    private int hightCriterion;
    private int widthCriterion;
    private int canvasHeight;
    private int canvasWidth;
    private int textFont;

    private String[] xdate;
    private int[] ydate;
    private float[] linedate;
    private int xCopies;
    private float yCopies;

    public void setChartdate(String[] xdate, int[] ydate, float[] linedate) {
        this.xdate = xdate;
        this.ydate = ydate;
        this.linedate = linedate;
    }


    public LineChartView(Context context) {
        super(context);
    }

    public LineChartView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (xdate.length!=0&&ydate.length!=0&&linedate.length!=0&&xdate.length>=linedate.length){
            if (yMaxdata()>=lineMaxdata()){
                drawAxis(canvas);
            }

        }

    }

    //绘制
    private void drawAxis(Canvas canvas) {
        xCopies = xdate.length + 2;
        yCopies = ydate.length + 2;
        Paint daxesPaint, axispointPaint, brokenLinePaint;
        //画布宽度
        canvasWidth = canvas.getWidth();
        //画布高度
        canvasHeight = canvas.getHeight();
        widthCriterion = canvasWidth / xCopies;
        hightCriterion = (int) (canvasHeight / yCopies);
        minCriterion = widthCriterion > hightCriterion ? hightCriterion / 2 : widthCriterion / 2;
        //开始绘制底层背景
        daxesPaint = new Paint();
        daxesPaint.setColor(Color.BLACK);
        daxesPaint.setAntiAlias(true);  //去掉锯齿效果
        daxesPaint.setStrokeWidth(7.0f);
        drawDaxes(canvas, daxesPaint);

        //开始绘制坐标点
        axispointPaint = daxesPaint;
        drawAxispoint(canvas, axispointPaint);

        //开始绘制折线和线上的点
        brokenLinePaint=axispointPaint;
        brokenLinePaint.setStrokeWidth(5.0f);
        drawbrokenLine(canvas,brokenLinePaint);
    }


    private void drawDaxes(Canvas canvas, Paint p) {
        //开始y绘制坐标系
        canvas.drawLine(widthCriterion, hightCriterion, widthCriterion, hightCriterion * (yCopies - 1), p);
        //绘制y角
        canvas.drawLine(widthCriterion - minCriterion, hightCriterion + minCriterion, widthCriterion + 2, hightCriterion, p);
        canvas.drawLine(widthCriterion, hightCriterion, widthCriterion + minCriterion - 2, hightCriterion + minCriterion, p);
        //开始x绘制坐标系
        canvas.drawLine(widthCriterion - 4, hightCriterion * (yCopies - 1), widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1), p);
        //绘制x角
        canvas.drawLine(widthCriterion * (xCopies - 1) - minCriterion, hightCriterion * (yCopies - 1) - minCriterion, widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1) + 2, p);
        canvas.drawLine(widthCriterion * (xCopies - 1) - minCriterion, hightCriterion * (yCopies - 1) + minCriterion, widthCriterion * (xCopies - 1), hightCriterion * (yCopies - 1) - 2, p);

    }

    private void drawAxispoint(Canvas canvas, Paint p) {
        textFont = widthCriterion / 5 * 2;
        Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
        p.setTypeface(font);
        p.setTextSize(textFont);
        //画x轴数据
        for (int i = 0; i < xdate.length; i++) {
            String text = xdate[i];
            int stringWidth = (int) p.measureText(text);     //文本长度
            canvas.drawText(text, (i + 1) * widthCriterion - stringWidth / 2, hightCriterion * (yCopies - 1) + textFont, p);// 画文本
        }
        for (int i = 0; i < ydate.length; i++) {
            String text = String.valueOf(ydate[i]);

            int stringWidth = (int) p.measureText(text);
            //文本长度
            if (i == 0) {
            } else {
                canvas.drawText(text, widthCriterion - textFont-stringWidth, hightCriterion * (yCopies - 1) - i * hightCriterion + stringWidth / 2, p);// 画文本

            }
        }
    }

    private void drawbrokenLine(Canvas canvas, Paint p) {
        float line=(hightCriterion * (yCopies - 1)-hightCriterion*2)/ydate[ydate.length-1];

        for (int i = 0; i <linedate.length; i++) {
            float height=hightCriterion * (yCopies-1)-line*linedate[i];
            if (i!=linedate.length-1){
                float elseheight=hightCriterion * (yCopies-1)-line*linedate[i+1];
                canvas.drawLine(widthCriterion*(i+1),height , widthCriterion * (i+2), elseheight, p);
                canvas.drawCircle(widthCriterion*(i+1), height, 10, p);
            }else{
                float endheight=hightCriterion * (yCopies-1)-line*linedate[linedate.length-1];
                canvas.drawCircle(widthCriterion*(i+1), endheight, 10, p);
            }

        }
    }
    private float yMaxdata(){
        float max = 0;
        for (int i = 0; i < ydate.length; i++) {

            if (ydate[i] > max) {
                max = ydate[i];

            }
        }
       return max;
    }

    private float lineMaxdata(){
        float max = 0;
        for (int i = 0; i < linedate.length; i++) {

            if (linedate[i] > max) {
                max = linedate[i];

            }
        }
        return max;
    }

}

核心:绘制与传入数据产生联系,建议先绘制一次固定的,再自我封装,有利于理解

觉得好的话,点个关注吧!谢谢
如有疑问,欢迎留言。

猜你喜欢

转载自blog.csdn.net/qq_42761395/article/details/106248327