一个安卓折线图控件

本文介绍了一个自定义的ChartView类,用于实现简单的折线图绘制功能。该类仅需一个ChartView.java文件即可完成基本的图表绘制,包括背景色块、坐标轴、刻度值、数据线条及数据点等,并提供了多条折线和单条折线的用法示例。

1.废话少说,看效果

图片1图片2

2.只需要一个class,即实现ChartView.java

public class ChartView extends View {

    private float xLength;
    private float yLength;
    private float startPointX;
    private float startPointY;
    private float xScale;
    private float yScale;
    private float coordTextSize;

    private String[] xLabel;
    private String[] yLabel;
    private ArrayList<String[]> data;
    private String[] title;

    //private String[] mDataLineColors;
    private int[] drawColors;
    private ArrayList<float[][]> mDataCoords = new ArrayList<float[][]>();

    private Paint mScaleLinePaint;
    private Paint mDataLinePaint;
    private Paint mScaleValuePaint;
    private Paint mBackColorPaint;

    Rect bounds = new Rect();

    private boolean isClick;                // 是否点击了数据点
    private int[] clickIndex;

    //private PopupWindow mPopWin;

    public ChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        this.setBackgroundColor(Color.WHITE);
        // x轴刻度值
        if (xLabel == null) {
            //获取当前日期
            xLabel = new String[7];
            long current = System.currentTimeMillis() / 1000;
            for (int i = 6; i >= 0; i--) {
                long tmp = current - 24 * 60 * 60 * (i + 1);
                String dateStr = Utils.longtimeToDayDate(tmp);
                xLabel[i] = dateStr.substring(5, dateStr.length());
            }
        }
        // 数据点
        if (data == null) {
            data = new ArrayList<String[]>();
            data.add(new String[]{"0", "0", "0", "0", "0", "0", "0"});

        }
        clickIndex = new int[data.size()];
        for (int i = 0; i < clickIndex.length; i++) {
            clickIndex[i] = -1;
        }
        // 标题
        if (title == null) {
            title = new String[]{"网点"};
        }
        // 根据设置的数据值生成Y坐标刻度值
        yLabel = createYLabel();
        //设置折线颜色
        if (drawColors == null) {
            drawColors = new int[1];
            drawColors[0] = Color.parseColor("#ff5400");
        }
        // 新建画笔
        mDataLinePaint = new Paint();       // 数据(点和连线)画笔
        mScaleLinePaint = new Paint();      // 坐标(刻度线条)值画笔
        mScaleValuePaint = new Paint();      // 图表(刻度值)画笔
        mBackColorPaint = new Paint();       // 背景(色块)画笔
        // 画笔抗锯齿
        mDataLinePaint.setAntiAlias(true);
        mScaleLinePaint.setAntiAlias(true);
        mScaleValuePaint.setAntiAlias(true);
        mBackColorPaint.setAntiAlias(true);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initParams();
        drawBackColor(canvas);              // 绘制背景色块
        drawYAxisAndXScaleValue(canvas);    // 绘制y轴和x刻度值
        drawXAxisAndYScaleValue(canvas);    // 绘制x轴和y刻度值
        drawDataLines(canvas);              // 绘制数据连线
        drawDataPoints(canvas);             // 绘制数据点
        drawTitle(canvas);                  // 绘制标题
    }

    /**
     * 绘制背景色块
     *
     * @param canvas
     */
    private void drawBackColor(Canvas canvas) {
        for (int i = 0; i < 7; i++) {
            if (i == 2 || i == 4 || i == 6) {
                canvas.drawRect(startPointX + (i - 1) * xScale,
                        startPointY,
                        startPointX + i * xScale,
                        yLength + startPointY,
                        mBackColorPaint);
            }
        }
    }

    private void drawYAxisAndXScaleValue(Canvas canvas) {
        for (int i = 0; i < 7; i++) {
            canvas.drawLine(startPointX + i * xScale,
                    startPointY,
                    startPointX + i * xScale,
                    startPointY + yLength,
                    mScaleLinePaint);
            mScaleValuePaint.getTextBounds(xLabel[i], 0, xLabel[i].length(), bounds);

            canvas.drawText(xLabel[i],
                    startPointX + i * xScale - bounds.width() / 2,
                    startPointY + yLength + bounds.height() + yScale / 15,
                    mScaleValuePaint);

        }
    }

    /**
     * 绘制x轴和y刻度值
     *
     * @param canvas
     */
    private void drawXAxisAndYScaleValue(Canvas canvas) {
        for (int i = 0; i < 6; i++) {
            if (i < 5) {
                mScaleValuePaint.getTextBounds(yLabel[4 - i], 0, yLabel[4 - i].length(), bounds);
                canvas.drawText(yLabel[4 - i],
                        startPointX - bounds.width() - 2 * xScale / 15,
                        startPointY + yScale * (i + 0.5f) + bounds.height() / 2,
                        mScaleValuePaint);
                canvas.drawLine(startPointX,
                        startPointY + (i + 0.5f) * yScale,
                        startPointX + xLength,
                        startPointY + (i + 0.5f) * yScale,
                        mScaleLinePaint);
            } else {
                canvas.drawLine(startPointX,
                        startPointY + (i + 0.5f) * yScale,
                        startPointX + xLength,
                        startPointY + (i + 0.5f) * yScale,
                        mScaleLinePaint);
            }
        }
    }

    /**
     * 绘制数据线条
     *
     * @param canvas
     */
    private void drawDataLines(Canvas canvas) {
        getDataRoords();
        for (int n = 0; n < data.size(); n++) {
            for (int i = 0; i < 6; i++) {
                mDataLinePaint.setColor(drawColors[n]);
                canvas.drawLine(mDataCoords.get(n)[i][0], mDataCoords.get(n)[i][1], mDataCoords.get(n)[i + 1][0], mDataCoords.get(n)[i + 1][1], mDataLinePaint);
                canvas.drawCircle(mDataCoords.get(n)[i][0], mDataCoords.get(n)[i][1], xScale / 15, mDataLinePaint);
            }
            canvas.drawCircle(mDataCoords.get(n)[6][0], mDataCoords.get(n)[6][1], xScale / 15, mDataLinePaint);
        }
    }

    /**
     * 绘制数据点
     *
     * @param canvas
     */
    private void drawDataPoints(Canvas canvas) {

        // 点击后,绘制数据点
        for (int n = 0; n < data.size(); n++) {
            if (isClick && clickIndex[n] > -1) {
                mDataLinePaint.setColor(drawColors[n]);
                canvas.drawCircle(mDataCoords.get(n)[clickIndex[n]][0], mDataCoords.get(n)[clickIndex[n]][1], xScale / 10, mDataLinePaint);
                mDataLinePaint.setColor(Color.WHITE);
                canvas.drawCircle(mDataCoords.get(n)[clickIndex[n]][0], mDataCoords.get(n)[clickIndex[n]][1], xScale / 20, mDataLinePaint);
                mDataLinePaint.getTextBounds(data.get(n)[clickIndex[n]], 0, data.get(n)[clickIndex[n]].length(), bounds);
                //画圆角矩形
                mDataLinePaint.setStyle(Paint.Style.FILL);//充满
                mDataLinePaint.setColor(drawColors[n]);//设置颜色
                // mDataLinePaint.setAntiAlias(true);// 设置画笔的锯齿效果
                float width = bounds.height() > bounds.width() ? bounds.height() :bounds.width();

                RectF rectF = new RectF(
                        mDataCoords.get(n)[clickIndex[n]][0] - 15 - width / 2,
                        mDataCoords.get(n)[clickIndex[n]][1] - 2 * bounds.height() - 17,
                        mDataCoords.get(n)[clickIndex[n]][0] + 15 + width / 2,
                        mDataCoords.get(n)[clickIndex[n]][1] - 2 * bounds.height() + bounds.height() + 13);// 设置个新的长方形
                canvas.drawRoundRect(rectF, rectF.height() / 2, rectF.height() / 2, mDataLinePaint);//第二个参数是x半径,第三个参数是y半径

                //绘制数据
                mDataLinePaint.setColor(Color.WHITE);
                canvas.drawText(data.get(n)[clickIndex[n]],
                        mDataCoords.get(n)[clickIndex[n]][0] - bounds.width() / 2 - 2,
                        mDataCoords.get(n)[clickIndex[n]][1] - 1 * bounds.height() - 2,
                        mDataLinePaint);
            }
        }
    }

    /**
     * 绘制标题
     *
     * @param canvas
     */
    private void drawTitle(Canvas canvas) {

        // 绘制标题文本和线条
        for (int i = 0; i < data.size(); i++) {
            //设置绘制标题时的颜色
            mDataLinePaint.setColor(drawColors[i]);
            mDataLinePaint.getTextBounds(title[i], 0, title[i].length(), bounds);
            canvas.drawText(title[i], startPointX + i * xLength / data.size() + xScale / 2 + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2, startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2 + bounds.height() / 2, mDataLinePaint);
            canvas.drawLine(startPointX + i * xLength / data.size() + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2,
                    startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2,
                    startPointX + i * xLength / data.size() + xScale / 2 - xScale / 15 + xLength / (2 * data.size()) - xScale / 2 - bounds.width() / 2,
                    startPointY + yLength + yScale / 2 + (yScale / 15 + coordTextSize) / 2,
                    mDataLinePaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float touchX = event.getX();
        float touchY = event.getY();
        for (int i = 0; i < data.size(); i++) {
            clickIndex[i] = -1;
        }
        for (int n = 0; n < data.size(); n++) {
            for (int i = 0; i < 7; i++) {
                float dataX = mDataCoords.get(n)[i][0];
                float dataY = mDataCoords.get(n)[i][1];
                // 控制触摸/点击的范围,在有效范围内才触发
                if (Math.abs(touchX - dataX) < xScale / 4 && Math.abs(touchY - dataY) < yScale / 4) {
                    isClick = true;
                    clickIndex[n] = i;
                    invalidate();
                    return true;
                }
            }
        }
        invalidate();

        return super.onTouchEvent(event);
    }

    private void initParams() {
        int width = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
        int height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();

        yScale = height / 7f;         // y轴刻度
        xScale = width / 7.5f;          // x轴刻度
        startPointX = xScale / 2;       // 开始绘图的x坐标
        startPointY = yScale / 2;       // 开始UI图的y坐标
        xLength = 6.5f * xScale;        // x轴长度
        yLength = 5.5f * yScale;        // y轴长度

        float chartLineStrokeWidth = xScale / 50;     // 图表线条的线宽
        coordTextSize = xScale / 5;             // 坐标刻度文字的大小
        float dataLineStrodeWidth = xScale / 15;      // 数据线条的线宽

        // 设置画笔相关属性
        mBackColorPaint.setColor(0x11DEDE68);
        mScaleLinePaint.setStrokeWidth(chartLineStrokeWidth);
        mScaleLinePaint.setColor(0xFFDEDCD8);
        mScaleValuePaint.setColor(0xFF999999);
        mScaleValuePaint.setTextSize(coordTextSize);
        mDataLinePaint.setStrokeWidth(dataLineStrodeWidth);
        mDataLinePaint.setStrokeCap(Paint.Cap.ROUND);
        mDataLinePaint.setTextSize(1.5f * coordTextSize);
    }

    /**
     * 根据数据值data生成合适的Y坐标刻度值
     *
     * @return y轴坐标刻度值数组
     */
    private String[] createYLabel() {
        if (data.isEmpty()) throw new NullPointerException("数据点为空");
        int num = data.size() * data.get(0).length;//所有的数据长度
        int[] dataFloats = new int[num];
        for (int i = 0; i < data.size(); i++) {
            String[] strs = data.get(i);
            for (int j = 0; j < strs.length; j++) {
                dataFloats[i * strs.length + j] = Integer.parseInt(strs[j]);
            }
        }
        // 将数据值从小到大排序
        Arrays.sort(dataFloats);
        // 中间值
        int middle = (dataFloats[0] + dataFloats[num - 1]) / 2;
        // Y刻度值
        int scale = (dataFloats[num - 1] - dataFloats[0] + 4) / 4;
        String[] yText = new String[5];
        yText[0] = (middle - 2 * scale) + "";
        yText[1] = (middle - scale) + "";
        yText[2] = middle + "";
        yText[3] = (middle + scale) + "";
        yText[4] = (middle + 2 * scale) + "";
        for (int i = 0; i < yText.length; i++) {
            yText[i] = yText[i] + "";
        }
        return yText;
    }

    /**
     * 获取数据值的坐标点
     *
     * @return 数据点的坐标
     */
    private void getDataRoords() {
        float originalPointX = startPointX;
        float originalPointY = startPointY + yLength - yScale;
        for (int n = 0; n < data.size(); n++) {
            float[][] tmp = new float[7][2];
            for (int i = 0; i < data.get(n).length; i++) {
                tmp[i][0] = originalPointX + i * xScale;
                float dataY = Float.parseFloat(data.get(n)[i]);
                float oriY = Float.parseFloat(yLabel[0]);
                tmp[i][1] = originalPointY - (yScale * (dataY - oriY) / (Float.parseFloat(yLabel[1]) - Float.parseFloat(yLabel[0])));
            }
            mDataCoords.add(tmp);
        }

    }

    /**
     * 格式化数字 ###.000
     *
     * @return ###.000
     */
   /* private float format3Bit(int number) {
        DecimalFormat decimalFormat = new DecimalFormat("###.000");
        String target = decimalFormat.format(number);
        if (target.startsWith(".")) {
            target = "0" + target;
        }
        return Float.parseFloat(target);
    }*/

    /**
     * 格式化数据 ###.000
     *
     * @param numberStr 格式化后的字符串形式
     * @return ###.000
     */
    /*private String format3Bit(String numberStr) {
        if (TextUtils.isEmpty(numberStr)) {
            return "0.000";
        }
        float numberFloat = Float.parseFloat(numberStr);
        DecimalFormat decimalFormat = new DecimalFormat("###.000");
        String target = decimalFormat.format(numberFloat);
        if (target.startsWith(".")) {
            target = "0" + target;
        }
        return target;
    }*/

    /**
     * 设置x轴刻度值
     *
     * @param xLabel x刻度值
     */
    public void setxLabel(String[] xLabel) {
        this.xLabel = xLabel;
    }

    /**
     * 设置数据
     *
     * @param data 数据值
     */
    public void setData(ArrayList<String[]> data) {
        this.data = data;
    }

    /**
     * 设置标题
     *
     * @param title 标题
     */
    public void setTitle(String[] title) {
        this.title = title;
    }

    /**
     * 重新设置x轴刻度、数据、标题后必须刷新重绘
     */
    public void fresh() {
        init();
        postInvalidate();
    }

    public void setDrawColors(int[] drawColors) {
        this.drawColors = drawColors;
    }

    public int[] getDrawColors() {
        return drawColors;
    }
}


3.用法

a.多条折线

/**
     * 填充数据到line_chart
     */
    private void setData() {
        String[] title = {"网点", "专线", "分拨中心"};//标题
        ArrayList<String[]> data = new ArrayList<String[]>();//数据
        data.add(new String[]{"6", "1", "9", "3", "0", "8", "5"});//第一条折线数据
        data.add(new String[]{"5", "1", "2", "6", "1", "7", "9"});//第二条折线数据
        String[] tmp = new String[data.get(0).length];//第三条折线数据
        
        for (int i = 0; i < data.get(0).length; i++) {
            tmp[i] = Integer.parseInt(data.get(0)[i]) + Integer.parseInt(data.get(1)[i]) + "";
        }
        data.add(tmp);
        //画折线的颜色
        int[] colors = new int[3];
        colors[0] = getResources().getColor(R.color.theme_bg);
        colors[1] = getResources().getColor(R.color.orange_f92);
        colors[2] = getResources().getColor(R.color.theme_red);

        mChartView1.setTitle(title);//设置标题
        mChartView1.setDrawColors(colors);//设置颜色
        mChartView1.setData(data);//设置数据
        mChartView1.fresh();//刷新折线图
    }

b.单条折线

   /**
     * 填充数据到line_chart
     */
    private void setData(ChartView c) {
        String[] title=new String[]{"宁波中通路桥店"};
        ArrayList<String[]> data = new ArrayList<String[]>();//数据
        int[] colors;
        colors=new int[]{mActivity.getResources().getColor(R.color.theme_bg)};
        data.add(new String[]{"6", "1", "9", "3", "0", "8", "5"});
        c.setTitle(title);//设置标题
        c.setDrawColors(colors);//设置颜色
        c.setData(data);//设置数据
        c.fresh();//刷新折线图
    }




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值