文档章节

自定义图表控件--同时显示柱状图和折线图

i
 i_love_lu
发布于 2016/01/29 09:33
字数 1950
阅读 129
收藏 7
public class MyChartView extends View {
    private static final int LEFT = 1;//在通用函数中用于区别左边y轴和右边y轴
    private static final int RIGHT = 2;
    private Paint mPaint;
    private int xNums;//x轴坐标分割点个数
    private int yNums;//y轴坐标分割点个数
    private String[] xTexts;//x坐标字段
    private String[] yBarTexts;//y坐标字段
    private String[] yLineTexts;//y坐标字段
    private double[] yBarUnits;//y坐标值
    private double[] yLineUnits;//y坐标值
    private int xStep;//x轴上每一个字段距离
    private int yStep;//y轴上每一个虚线距离
    private int yBarStepVal;//y轴字段间距值
    private int yLineStepVal;//y轴字段间距值
    private int width;//获取控件宽度
    private int height;//获取控件高度
    private boolean isBarPercent = false;//检测yStepVal是不是百分数
    private boolean isLinePercent = false;//检测yStepVal是不是百分数
    private int canDrawMaxNum = 0;//根据屏幕宽度设置x轴最多可显示的字段数
    private float xOffset = 0;//当前的位置
    private float startX;//手指按下位置
    private int MAX_OFFSET;//移动范围
    private float xOffsetThisTime;//现在要移动的值
    private boolean isFirstInitDraw = true;//是否第一次画图,使图表初始化在最后数据的位置
    private boolean isMoveable = false;//检查是不是需要滑动

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initDraw();
        drawAxis(canvas);
        drawYAxisText(canvas);
        drawHLines(canvas);
        Log.d("xOffset = ", xOffset + "");

        if (isMoveable) {
            //移动绘制画柱状图和折线图区域,移动绘制x轴文字区域
            canvas.save();
            canvas.clipRect(dp2px(30) + dp2px(3), dp2px(30) + dp2px(1), width - dp2px(30) - dp2px(3), height + dp2px(30), Region.Op.REPLACE);
            canvas.translate(xOffset, 0);
            drawBarChart(canvas);
            drawXAxisText(canvas);
            drawLineChart(canvas);
            canvas.restore();
        } else {
            //不用移动时候直接绘制内容
            drawBarChart(canvas);
            drawXAxisText(canvas);
            drawLineChart(canvas);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isMoveable) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    startX = (int) event.getRawX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    float stopX = (int) event.getRawX();
                    xOffsetThisTime = stopX - startX;
                    xOffset += xOffsetThisTime;
                    startX = stopX;

                    invalidate();
                    isFirstInitDraw = false;
                    break;
                case MotionEvent.ACTION_UP:
                    //smooth scroll
                    new Thread(new SmoothScrollThread(xOffsetThisTime)).start();
                    break;
            }
            return true;
        } else {
            return false;
        }

    }

    /**
     * 初始化onDraw函数中需要的一些变量
     */
    private void initDraw() {
        mPaint = new Paint();
        width = getWidth();
        height = getHeight() - dp2px(30);
        canDrawMaxNum = (width - dp2px(60)) / dp2px(30);
        Log.d("canDrawMaxNum", canDrawMaxNum + "");
        //设置y坐标字段,尽量以整数来分割
        setYTexts(yBarUnits, LEFT);
        setYTexts(yLineUnits, RIGHT);
        for (int i = 0; i < yLineUnits.length; i++) {
            Log.d("yLineUnits" + i + " == ", "" + yLineUnits[i]);
        }
        //如果字段数比canDrawMaxNum小,则将字段加宽以适应屏幕
        if (yBarTexts.length - 1 < canDrawMaxNum) {
            xStep = (width - dp2px(60)) / (yBarTexts.length - 1);
        } else {
            xStep = (width - dp2px(60)) / canDrawMaxNum;
        }
        yStep = (height - dp2px(30)) / (yBarTexts.length - 1);

        MAX_OFFSET = (dp2px(30) + xStep / 2 * (1 + 2 * (xTexts.length - 1)) + dp2px(10) + dp2px(10)) -
                (dp2px(30) + xStep / 2 - dp2px(10)) - (width - dp2px(60));
        if (isFirstInitDraw) {
            xOffset = -MAX_OFFSET;
        }
    }

    /**
     * 画坐标轴
     *
     * @param canvas
     */
    private void drawAxis(Canvas canvas) {
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(1);

        //画x轴
        canvas.drawLine(dp2px(30), height, width - dp2px(30), height, mPaint);

        //画左边y轴
        canvas.drawLine(dp2px(30), height, dp2px(30), dp2px(10), mPaint);
        //画左边y轴箭头
        canvas.drawLine(dp2px(30), dp2px(10), dp2px(27), dp2px(13), mPaint);
        canvas.drawLine(dp2px(30), dp2px(10), dp2px(33), dp2px(13), mPaint);

        //画右边y轴
        canvas.drawLine(width - dp2px(30), height, width - dp2px(30), dp2px(10), mPaint);
        //画右边y轴箭头
        canvas.drawLine(width - dp2px(30), dp2px(10), width - dp2px(27), dp2px(13), mPaint);
        canvas.drawLine(width - dp2px(30), dp2px(10), width - dp2px(33), dp2px(13), mPaint);
    }

    /**
     * 画x轴坐标点处的文字
     *
     * @param canvas
     */
    private void drawXAxisText(Canvas canvas) {

        Log.d("yStep text = ", yStep + "");
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setTextSize(sp2px(8));
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(2);
        for (int i = 0; i < xTexts.length; i++) {
            canvas.drawText(xTexts[i], dp2px(30) + xStep / 2 * (1 + 2 * i) - dp2px(10), height + dp2px(10), mPaint);
        }
    }

    /**
     * 画y轴坐标点处的文字
     *
     * @param canvas
     */
    private void drawYAxisText(Canvas canvas) {

        Log.d("yStep text = ", yStep + "");
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.LEFT);
        mPaint.setTextSize(sp2px(8));
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(2);

        for (int i = 0; i < yBarTexts.length; i++) {
            canvas.drawText(yBarTexts[i], dp2px(10), height - (i) * yStep + dp2px(3), mPaint);
        }
        for (int i = 0; i < yLineTexts.length; i++) {
            canvas.drawText(yLineTexts[i], width - dp2px(30) + dp2px(10), height - (i) * yStep + dp2px(3), mPaint);
        }
    }

    /**
     * 画水平线
     *
     * @param canvas
     */
    private void drawHLines(Canvas canvas) {
        Log.d("yStep  Line= ", yStep + "");
        mPaint.setAntiAlias(true);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setColor(Color.LTGRAY);
        mPaint.setStrokeWidth(1);
        for (int i = 0; i < yBarTexts.length - 1; i++) {
            canvas.drawLine(dp2px(30), height - (i + 1) * yStep, width - dp2px(30), height - (i + 1) * yStep, mPaint);
        }
    }

    /**
     * 画柱状图
     *
     * @param canvas
     */
    private void drawBarChart(Canvas canvas) {
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.CYAN);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);

        for (int i = 0; i < yNums; i++) {
            float drawHeight;
            if (!isBarPercent) {
                if (!yBarTexts[0].equals("0")) {
                    drawHeight = (float) (height - yStep * ((yBarUnits[i] - yBarStepVal) / yBarStepVal));
                } else {
                    drawHeight = (float) (height - yStep * (yBarUnits[i] / yBarStepVal));
                }
            } else {
                if (!yBarTexts[0].equals("0")) {
                    drawHeight = (float) (height - (float) yStep * ((yBarUnits[i] * 100 - yBarStepVal) / yBarStepVal));
                } else {
                    drawHeight = (float) (height - (float) yStep * (yBarUnits[i] * 100 / yBarStepVal));
                }
            }
            canvas.drawRect(dp2px(30) + xStep / 2 * (1 + 2 * i) - dp2px(10), drawHeight, dp2px(30) + xStep / 2 * (1 + 2 * i) + dp2px(10), height, mPaint);
        }
    }

    /**
     * 画折线图
     *
     * @param canvas
     */
    private void drawLineChart(Canvas canvas) {
        mPaint.setAntiAlias(true);

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        float x = 0, y = 0;
        for (int i = 0; i < yNums; i++) {
            float drawHeight;
            if (!isLinePercent) {
                if (!yLineTexts[0].equals("0")) {
                    drawHeight = (float) (height - yStep * ((yLineUnits[i] - yLineStepVal) / yLineStepVal));
                } else {
                    drawHeight = (float) (height - yStep * (yLineUnits[i] / yLineStepVal));
                }
            } else {
                if (!yLineTexts[0].equals("0")) {
                    drawHeight = (float) (height - (float) yStep * ((yLineUnits[i] * 100 - yLineStepVal) / yLineStepVal));
                } else {
                    drawHeight = (float) (height - (float) yStep * (yLineUnits[i] * 100 / yLineStepVal));
                }
            }
            mPaint.setColor(Color.MAGENTA);
            canvas.drawCircle(dp2px(30) + xStep / 2 * (1 + 2 * i), drawHeight, 5, mPaint);
            if (i > 0) {
                mPaint.setColor(Color.BLUE);
                canvas.drawLine(x, y, dp2px(30) + xStep / 2 * (1 + 2 * i), drawHeight, mPaint);
            }
            x = dp2px(30) + xStep / 2 * (1 + 2 * i);
            y = drawHeight;
//            if (i>0){
//                canvas.drawLine();
//            }
        }
    }

    /**
     * 坐标转换,从dp转换为px
     *
     * @param value
     * @return
     */
    private int dp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().density;
        return (int) (v * value + 0.5f);
    }

    /**
     * 坐标转换,从sp转换为px
     *
     * @param value
     * @return
     */
    private int sp2px(int value) {
        float v = getContext().getResources().getDisplayMetrics().scaledDensity;
        return (int) (v * value + 0.5f);
    }

    /**
     * 从服务器或其他地方传入数据,由此函数适配
     *
     * @param object 传入数据以Json格式传入,Json格式如下:
     *               {
     *               "chart":{
     *               "xunits":["xUnit1","xUnit2",...],
     *               "ybarunits":["yUnit1","yUnit2",...],
     *               "ylineunits":["yUnit1","yUnit2",...]
     *               }
     */
    public void setData(JSONObject object) {
        try {
            //解析Json数据
            JSONObject result = object.getJSONObject("chart");
            JSONArray xArray = result.getJSONArray("xunits");
            JSONArray yBarArray = result.getJSONArray("ybarunits");
            JSONArray yLineArray = result.getJSONArray("ylineunits");
            Log.d("barArray = ", yBarArray.toString());
            Log.d("lineArray = ", yLineArray.toString());

            //部署数据
            xNums = yNums = xArray.length();
            xTexts = new String[xNums];

            xTexts = parseJsonArray2StringArray(xArray);
            yBarUnits = new double[yNums];
            yLineUnits = new double[yNums];
            String[] tempBar = parseJsonArray2StringArray(yBarArray);
            String[] tempLine = parseJsonArray2StringArray(yLineArray);
            for (int i = 0; i < yNums; i++) {
                yBarUnits[i] = Double.parseDouble(tempBar[i]);
                yLineUnits[i] = Double.parseDouble(tempLine[i]);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    /**
     * 从服务器或其他地方传入数据,由此函数适配
     *
     * @param xUnits     x轴数据,传入字段名称数组
     * @param yBarUnits  y轴数据,传入柱状图字段值数组
     * @param yLineUnits y轴数据,传入折线图字段值数组
     */
    public void setData(String[] xUnits, String[] yBarUnits, String[] yLineUnits) {
        //部署数据
        xNums = yNums = xUnits.length;
        xTexts = new String[xNums];

        xTexts = xUnits;
        this.yBarUnits = new double[yNums];
        this.yLineUnits = new double[yNums];
        for (int i = 0; i < yNums; i++) {
            this.yBarUnits[i] = Double.parseDouble(yBarUnits[i]);
            this.yLineUnits[i] = Double.parseDouble(yLineUnits[i]);
        }
    }

    /**
     * 设置y坐标字段,尽量以整数来分割
     * y坐标字段最多设置为x轴每个柱状图之间有间隔
     *
     * @param yUnits
     * @param which  控制是左边还是右边
     */
    private void setYTexts(double[] yUnits, int which) {
        String[] yTempTexts;
        int yTempStepVal;
        boolean isTempPercent;
        if (yUnits.length > canDrawMaxNum) {
            yTempTexts = new String[canDrawMaxNum + 1];
            isMoveable = true;
        } else {
            yTempTexts = new String[yUnits.length + 1];
            isMoveable = false;
        }
        Log.d("yTempTexts.length = ", yTempTexts.length + "");

        //获取yUnits的最大值和最小值
        double max = 0;
        double min = yUnits[0];
        for (int i = 0; i < yUnits.length; i++) {
            if (max < yUnits[i]) {
                max = yUnits[i];
            }
            if (min > yUnits[i]) {
                min = yUnits[i];
            }
        }
        Log.d("max, min ", max + ", " + min);
        //将最大值与最小值的差等分成yUnits.length份,并取相近的整数代替
        int times = yUnits.length > canDrawMaxNum ? yUnits.length / canDrawMaxNum : 1;
        yTempStepVal = (int) (((max - min) / yUnits.length + 1) * times);
        //判断yTempStepVal是不是小于最小值
        while (yTempStepVal * yTempTexts.length < max) {
            yTempStepVal++;
        }
        if (yTempStepVal < min) {
            //可以显示出最小值,不需要再处理了
            if (yTempStepVal >= 2) {
                isTempPercent = false;
                Log.d("yTempStepVal = ", yTempStepVal + "");
                for (int i = 0; i < yTempTexts.length; i++) {
                    yTempTexts[i] = String.valueOf(yTempStepVal * (i + 1));
                }
            } else {
                isTempPercent = true;
                yTempStepVal = (int) (((max - min) / yUnits.length) * 100 + 1);
                Log.d("yTempStepValP = ", yTempStepVal + "");
                for (int i = 0; i < yTempTexts.length; i++) {
                    yTempTexts[i] = yTempStepVal * (i + 1) + "%";
                }
            }
            Log.d("isTempPercent = ", isTempPercent + "");
        } else {
            //无法显示最小值,则起点从0开始,再重新调整yTempStepVal的值
            while (yTempStepVal * (yTempTexts.length - 1) < max) {
                yTempStepVal++;
            }

            if (yTempStepVal >= 2) {
                isTempPercent = false;
                Log.d("yTempStepVal = ", yTempStepVal + "");
                yTempTexts[0] = String.valueOf(0);
                for (int i = 1; i < yTempTexts.length; i++) {
                    yTempTexts[i] = String.valueOf(yTempStepVal * i);
                }
            } else {
                isTempPercent = true;
                yTempStepVal = (int) (((max - min) / yUnits.length) * 100 + 1);
                Log.d("yTempStepValP = ", yTempStepVal + "");
                yTempTexts[0] = String.valueOf(0);
                for (int i = 1; i < yTempTexts.length; i++) {
                    yTempTexts[i] = yTempStepVal * (i + 1) + "%";
                }
            }
            Log.d("isTempPercent = ", isTempPercent + "");
        }

        if (which == LEFT) {
            //左边
            yBarTexts = yTempTexts;
            yBarStepVal = yTempStepVal;
            isBarPercent = isTempPercent;
        }
        if (which == RIGHT) {
            //右边
            yLineTexts = yTempTexts;
            yLineStepVal = yTempStepVal;
            isLinePercent = isTempPercent;
        }
    }

    /**
     * 将Json数组转换成字符串数组以方便操作
     *
     * @param jsonArray
     * @return
     * @throws JSONException
     */
    private String[] parseJsonArray2StringArray(JSONArray jsonArray) throws JSONException {
        String[] array = new String[jsonArray.length()];
        for (int i = 0; i < jsonArray.length(); i++) {
            array[i] = jsonArray.getString(i);
        }
        return array;
    }

    /**
     * 自定义线程,用以控制图表移动速度和移动逻辑
     */
    private class SmoothScrollThread implements Runnable {
        float xOffsetThisTime;
        boolean scrolling = true;

        public SmoothScrollThread(float xOffsetThisTime) {
            this.xOffsetThisTime = xOffsetThisTime;
            scrolling = true;
        }

        @Override
        public void run() {
            while (scrolling) {
                long start = System.currentTimeMillis();
                xOffsetThisTime = (int) (0.9f * xOffsetThisTime);
                xOffset += xOffsetThisTime;

                checkXOffset();
                postInvalidate();
                isFirstInitDraw = false;
                if (Math.abs(xOffsetThisTime) < 20) {
                    scrolling = false;
                }

                long end = System.currentTimeMillis();
                if (end - start < 20) {
                    try {
                        Thread.sleep(20 - (end - start));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 检查移动是否在可移动范围内
     */
    private void checkXOffset() {
        if (xOffset > 0) {
            xOffset = 0;
        }

        if (xOffset < -MAX_OFFSET) {
            xOffset = -MAX_OFFSET;
        }
    }
}


© 著作权归作者所有

i
粉丝 1
博文 8
码字总数 4816
作品 0
石家庄
私信 提问
思达BI软件StyleIntelligence实例教程—房地产分析

本例将使用思达商业智能平台Style Intelligence对房地产业进行分析。 新建工作表,导入数据源。数据为2014年1月-2015年6月房地产开发及销售情况的数据 一、房地产开发情况 拖拽图标控件到编辑...

敏捷商业智能
2015/07/22
0
0
思达BI软件StyleIntelligence实例教程—股票分析(2)

教程视频:http://v.youku.com/vshow/idXMTI4Mzk0MjEzMg==.html 本文将在思达商业智能平台Style Intelligence对15年6月29日至15年7月8日股市情况进行分析。 新建工作表,导入数据,如图: 一...

敏捷商业智能
2015/07/16
0
0
canvas图表(2) - 折线图

原文地址:canvas图表(2) - 折线图 话说这天气一冷啊, 就患懒癌, 就不想码代码, 就想着在床上舒舒服服看视频. 那顺便就看blender视频, 学习下3D建模, 如果学会了建3D模型, 那我的webGL技术就...

Jeff.Zhong
2017/11/21
0
0
《101 Windows Phone 7 Apps》读书笔记-Weight Tracker

课程内容 Ø Charts & Graphs 你平时关注自己的体重吗?Weight Tracker使得你可以随时跟踪自己的体重,并且提供几种体重发展趋势的视图。它是一个基于Pivot控件的、具有三条Pivot Item的应用...

长平狐
2012/08/21
327
0
阿杜杜不是阿木木/highcharts2javatag

highcharts2javatag 通过自定义标签实现highcharts 3D图表展示,借鉴了Android适配器的思想,通过固定的数据源,展现相关图表,无需考虑图表内部实现。 演示地址: http://dtmonitor.tunnel.q...

阿杜杜不是阿木木
2016/04/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

HeyUI组件库按需加载功能上线,盘点HeyUI组件库有哪些独特功能?

HeyUI组件库 如果你还不了解heyui组件库,欢迎来我们的官网或者github参观。 官网 github 当然,如果能给我们一颗✨✨✨,那是最赞的了! 按需加载 当heyui组件库的组件越来越多的时候,按需...

vvpvvp
13分钟前
2
0
Dockerfile文件详解

Dockerfile文件详解 什么是dockerfile? Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。 docker bui...

Jeam_
26分钟前
0
0
阿里云PolarDB发布重大更新 支持Oracle等数据库一键迁移上云

5月21日,阿里云PolarDB发布重大更新,提供传统数据库一键迁移上云能力,可以帮助企业将线下的MySQL、PostgreSQL和Oracle等数据库轻松上云,最快数小时内迁移完成。据估算,云上成本不到传统...

zhaowei121
34分钟前
0
0
在数据数据探索过程中的一些常用操作

###pandas在做数据探索时,分组统计均值和中位数参考资料:http://www.cnblogs.com/nxld/p/6058591.htmlhttp://python.jobbole.com/85742/按字典重新赋值,可以直接使用pandas中的repla...

KYO4321
37分钟前
0
0
好程序员分享干货 弹性分布式数据集RDD

一、RDD定义 RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变(数据和元数据)、可分区、里面的元素可并行计算的集合。其特点在于自动容...

好程序员IT
38分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部