android之自定义View和ViewGroup(六)(代码篇,实现简单的走势图,柱状图同理)

项目需要走势图,什么柱状图、走势图,网上都有一堆现成的框架,但是我项目中只需要走势图,我用第三方框架简直是累赘,毕竟占大小,而且走势图也简单,所以就自己写了,好久没写博客了,就记下吧,供大家参考下,其实很简单,大佬勿喷。先不废话,看效果图吧:
这里写图片描述
记录了七天的走势,原点的x坐标不是0,因为我们不是用来做坐标轴,而是记录数据,原点的坐标根据实际需求情况而定的。

好了,废话说完了,开始动手吧,动手之前我们得先想想怎么实现,思路是怎样的。

首先,我们需要知道我们需要画什么。
1.我们需要画x,y轴;
2.我们需要画x,y轴的刻度和刻度值;
3.我们需要画y轴上的每个刻度的横线;
4.我们需要画数据的点;
5.数据的点需要连线;
大致就是这些,还有些文字的大小啊,颜色啊等等,都可以自己设置;

然后,我们需要知道怎么画。
大家都知道在视图坐标系中,一个view,原点是它的左上角,所以我们需要实际的原点,真实的原点,即走势图中x,y轴的交接点。所以我们得先确定实际原点,假设实际原点的坐标是(xo,yo),averageX代表x轴每个刻度间的距离,那图片中x轴的11.2所在的坐标就是相对于原点的(averageX,0),所以实际坐标就是(xo+averageX,yo+0)。那么y轴的1000所在的坐标就是相对于原点的(0,-averageY),所以实际坐标是(0+xo,-averageY+yo)注意,android中,x轴向右为正,y轴向下为正,所以1000所在的坐标是负的。哎,语文没学好,可能表达有点不清楚?总之呢,就是为了确定每个点的算法才这样计算的。

首先,我们自定义View继承自View,然后相关属性:

private Context context;
    //x,y轴的画笔,刻度文字的画笔,数据点的画笔,数据连线的画笔,y轴刻度横线的画笔,数据点文字的画笔
    private Paint xyPaint, textPaint, pointPaint, pointslinePaint, yLinePaint, pointTextPaint;
    private int padding;  //内部距离,设置个默认的padding值
    private int width, height;  //控件实际高度和宽度
    private float xSize, ySize;//x和y轴的长度(因为控件留了内部距离,不然x,y轴就贴着控件边缘导致看不清,有内部距离,所以长度和控件的宽高不一样了)
    private int defaultWidth;  //默认宽度
    private int defaultHeight;  //默认高度
    private int xOrigin, yOrigin;  //相对原点的坐标
    private String[] xPoints;  //x轴显示的坐标
    private String[] yPoints;  //y轴显示的坐标
    private List<Float> xPointList, yPointList; //数据点的x坐标集合,y坐标集合;

然后初始化画笔,默认高宽等等:

private void init() {
        xPointList = new ArrayList<>();
        yPointList = new ArrayList<>();
        defaultWidth = (DisplayUtils.width > 480 ? 480 : DisplayUtils.width);
        defaultHeight = 320;
        padding = DisplayUtils.dip2px(context, 35);

        xyPaint = new Paint();
        xyPaint.setStyle(Paint.Style.FILL);
        xyPaint.setAntiAlias(true);

        textPaint = new Paint();
        textPaint.setStyle(Paint.Style.FILL);
        textPaint.setAntiAlias(true);
        textPaint.setTextSize(DisplayUtils.dip2px(context, 10));

        pointPaint = new Paint();
        pointPaint.setStyle(Paint.Style.FILL);
        pointPaint.setAntiAlias(true);

        pointslinePaint = new Paint();
        pointslinePaint.setStyle(Paint.Style.FILL);
        pointslinePaint.setAntiAlias(true);
        pointslinePaint.setStrokeWidth(6);

        yLinePaint = new Paint();
        yLinePaint.setStyle(Paint.Style.FILL);
        yLinePaint.setAntiAlias(true);

        pointTextPaint = new Paint();
        pointTextPaint.setStyle(Paint.Style.FILL);
        pointTextPaint.setAntiAlias(true);
        pointTextPaint.setTextSize(DisplayUtils.dip2px(context, 6));
    }

为了简单,我就不再attr中设置属性了,直接在代码里面设置颜色值之类的,大家知道思路就好。

设置一个public方法供外部调用来设置数据点的坐标,我们用Map来保存坐标:

public void setData(String[] xPoints, String[] yPoints, Map<Float, Float> showPointsMap) {
        this.xPoints = xPoints;
        this.yPoints = yPoints;
        for (float x : showPointsMap.keySet()) {
            xPointList.add(x);
            yPointList.add(showPointsMap.get(x));
        }
    }

设置一个public方法供外部调用,来设置画笔的颜色:

//x,y轴和刻度文字的画笔,数据点的画笔,数据连线的画笔,y轴刻度横线的画笔,数据点文字的画笔
    public void setColor(int xyColor, int pointColor, int lineColor, int yLineColor, int pointTextColor) {
        xyPaint.setColor(xyColor);
        textPaint.setColor(xyColor);
        pointPaint.setColor(pointColor);
        pointslinePaint.setColor(lineColor);
        yLinePaint.setColor(yLineColor);
        pointTextPaint.setColor(pointTextColor);
    }

然后接下来开始自定义的方法,先onMeasure()方法确定大小,如果测量模式不是EXACYLY,那么就使用默认的高宽值:

if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = defaultWidth;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = defaultHeight;
        }
        ySize = height - padding - 60;  //y轴总长度,y轴的paddingTop我只留60px,paddingButtom留padding大小,可以自己定义
        xSize = width - padding * 2;  //x轴总长度,x方向的左右padding都设置为padding大小
        xOrigin = padding;  //实际原点的x坐标
        yOrigin = height - padding;  //实际原点的y坐标
        setMeasuredDimension(width, height);

然后重要的onDraw()方法:

float averageHeight = ySize / 5;  //y轴每个刻度之间的长度,设置6个刻度
        float averageWidth = xSize / 6;  //x轴每个刻度之间的长度,设置7个刻度
        float averagePx = ySize / 5000;
        canvas.drawLine(xOrigin, yOrigin, xOrigin + xSize + 15, yOrigin, xyPaint);  //画x轴,多画了15px让x轴右边可以出来,不然顶部正好是最后一个刻度线
        canvas.drawLine(xOrigin, yOrigin, xOrigin, yOrigin - ySize - 15, xyPaint);  //画y轴,多画了15px让y轴上边可以出来,不然顶部正好是最后一个刻度线
        for (int i = 0; i < yPoints.length; i++) {  //画y轴上的刻度,y轴刻度的横线,y轴刻度文字
            canvas.drawLine(xOrigin, yOrigin - averageHeight * (i + 1), xOrigin - 15, yOrigin - averageHeight * (i + 1), xyPaint);
            canvas.drawLine(xOrigin, yOrigin - averageHeight * (i + 1), xOrigin + xSize + 15, yOrigin - averageHeight * (i + 1), yLinePaint);
            canvas.drawText(yPoints[i], 10, yOrigin - averageHeight * (i + 1), textPaint);
        }
        for (int i = 0; i < xPoints.length; i++) {  //画x轴上的刻度和文字
            canvas.drawLine(xOrigin + averageWidth * i, yOrigin, xOrigin + averageWidth * i, yOrigin + 15, xyPaint);
            canvas.drawText(xPoints[i], xOrigin + averageWidth * (i), height - 30, textPaint);
        }
        for (int i = 0; i < xPointList.size(); i++) {  //画走势连线
            if (i < xPointList.size() - 1) {
                canvas.drawLine(xOrigin + averageWidth * (i), yOrigin - averagePx * yPointList.get(i),
                        xOrigin + averageWidth * (i + 1), yOrigin - averagePx * yPointList.get(i + 1), pointslinePaint);
            }
        }
        for (int i = 0; i < xPointList.size(); i++) {  //画数据点,先画线是为了让点可以盖住线
            canvas.drawCircle(xOrigin + averageWidth * (i), yOrigin - averagePx * yPointList.get(i), 10, pointPaint);  //数据点
            canvas.drawText(yPointList.get(i) + "", xOrigin + averageWidth * (i) + 20, yOrigin - averagePx * yPointList.get(i), pointTextPaint); //数据文字
        }

思路其实很简单,也没任何难点,就是画点,画线,画刻度。需要说的都在代码中写明了,就不啰嗦了,主要是知道思路,剩下的就简单了。

其实还有很多可以优化的地方,比如自定义属性颜色值,字体大小,线条粗细,数据点的大小,刻度值的位置等等,都可以自定义,这些比较简单我就不啰嗦了,有兴趣的朋友可以去试试。对于柱状图,其实一样的原理。

发布了33 篇原创文章 · 获赞 49 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/gsw333/article/details/70243261
今日推荐