Android自定义折线图

本文介绍了如何在Android中自定义折线图视图,包括重写`onMeasure`方法来确定尺寸,以及按顺序绘制坐标轴、文字和折线的详细步骤。通过设置属性值和提供设置数据的方法,实现了自定义折线图的功能。
摘要由CSDN通过智能技术生成

前言

折线图在比较数据、天气等方面的时候会用到,网上也不乏大佬将其封装成完整控件。自己也简单写写看一下效果。先上效果图,毕竟无图何以言diao。

嗯,效果就这样,下面将介绍其从无到有的过程!!

重写View的onMeasure方法

在自定义控件的时候,控件的宽高(最终的测量尺寸)由控件本身和其父容器共同决定的,既然是共同决定的我们得知道父容器的“意愿”对吧!

父容器的“意愿”也就三种,在MeasureSpec类中的三个常量表明了其三种意愿:

1、UNSPECIFIED:父容器对控件的大小不在意,你想多大都可以;

2、AT_MOST:父容器给控件的大小设置了一个最大值,表示你大小的取值最好在这个范围内;

3、EXACTLY: 父容器已经给控件计算出了显示的大小,控件就显示这么大得了;

onMeasure方法具体的代码:

    int resultWidth;
    //获取父容器的意愿
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);

    if (widthMode == MeasureSpec.EXACTLY) {
        resultWidth = sizeWidth;
    } else {
        if (widthMode == MeasureSpec.AT_MOST) {
            resultWidth = Math.min(screenWidth, sizeWidth);
        } else {
            resultWidth = screenWidth;
        }
    }
    int resultHeight;

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

    if (heightMode == MeasureSpec.EXACTLY) {
        resultHeight = sizeHeight;
    } else {
        if (heightMode == MeasureSpec.AT_MOST) {
            resultHeight = Math.min(screenHeight, sizeHeight);
        } else {
            resultHeight = screenHeight;
        }
    }
    //设置测量出的宽高
    setMeasuredDimension(resultWidth, resultHeight);

因为折线图是一个显示出来较占位置的控件,所以在父容器对大小无要求的时候,就以屏幕的大小作为宽高。获取屏幕的宽高:

DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    screenWidth = metrics.widthPixels;
    screenHeight = metrics.heightPixels;

现在展现在你面前的就是一张“白纸”,想怎么画全看意愿了,哈

理一下画图的顺序

drawTextY(Canvas canvas) //绘制y轴上需要显示的文字
drawTextX(Canvas canvas) //绘制x轴上的文字
drawCoordinate(Canvas canvas) //绘制坐标轴
drawBrokenLine(Canvas canvas) //绘制折线、顶点及顶点文字
差不多就是这个从上到下的顺序!
看到这儿估计有人会说:你特么逗我么,绘制折线图不先画坐标轴?
道友别急,请听我狡辩:绘制折线图有一个关键点是确定点的坐标(特么不是废话么),之所以将坐标轴放到文字之后绘制是因为y上绘制文字的宽度和x轴上文字的高度会对坐标轴的定位有一定影响。因为这两个数据是未知的,所以我们得先确定下来才好进行下一步操作!

drawTextY(Canvas canvas)

先上代码

//y轴文字的最大宽度
private int maxTextWidth;
//y轴每一刻度的高度
private int singleHeight;
//文字的高度
private int textHeight;

//y轴文字list
private List<String> textY;

/**
 * draw Y轴上的文字
 *
 * @param canvas
 */
private void drawTextY(Canvas canvas) {
    if (textY == null) return;

    // 文字的高度:mTextPaint.descent() - mTextPaint.ascent()
    textHeight = (int) (mTextPaint.descent() - mTextPaint.ascent());

    singleHeight = (getHeight() - paddingBottom - paddingTop - textHeight - spacing) / (textY.size() - 1);

    int xPos = paddingLeft;
    int yPos;
    for (int i = 0; i < textY.size(); i++) {
        //找出文字所需的最大宽度
        if (mTextPaint.measureText(textY.get(textY.size() - i - 1)) > maxTextWidth) {
            maxTextWidth = (int) mTextPaint.measureText(textY.get(textY.size() - i - 1));
        }

        yPos = paddingTop + singleHeight * i + textHeight / 2;

        canvas.drawText(textY.get(textY.size() - i - 1), xPos, yPos, mTextPaint);
    }
}

变量注释应该还算详细,抽两个简单介绍一下吧
textHeight:文字的高度获取,mTextPaint.descent() - mTextPaint.ascent()表达式前后的顺序不要反了,不然得到是一个负数(别问我是怎么知道的)。至于为什么是这样获取,问神奇的www.google.cn吧。
singleHeight:每一刻度的高度,通过控件的高度减去上下边距、文字高度、文字与坐标轴的间距(固定值)除以份数得到,聪明的你一定了然于心了!

drawTextX(Canvas canvas)

//x轴每一刻度的宽度
private int singleWidth;
//x轴文字list
private List<String> textX;

/**
 * draw X轴上的文字
 *
 * @param canvas
 */
private 
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值