MPAndroidChart(4)——柱状图BarChart的使用(多条柱状图)

本文相关代码

MPAndroidChart在github上地址:https://github.com/PhilJay/MPAndroidChart

目标效果图为某公司的净资产收益率:

7.png
1. 数据准备

1.1 数据来源

数据是抓包佣金宝的数据,将获取的数据存入.json文件。
https://gitee.com/zyd_gitee/android-mp-charts/blob/master/app/src/main/assets/line_chart.json

1.png

2. 图表显示

2.1 MPAndroidChart获取

Github 地址:https://github.com/PhilJay/MPAndroidChart

依赖:
Project 的build.gradle文件中添加


2.png

然后在Project 的settings.gradle 中添加


3.png

然后在 module中的build,gradle 中添加
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'

2.2 数据对象获取
在Android Studio app项目src同级目录下新建中新建assets文件夹,然后将步骤1.1得到的.json文件放入改文件夹中。

然后在获取.json文件中的json字符串并解析为对应的数据对象,可参考以下文章

相关文章:Android访问assets本地Json文件[[(18条消息) Android访问assets本地Json文件

在此就不详叙述了,只是列出图表展示所需要的类

    /**
     * 公司净资产收益率
     */
    public static class VtDateValueBean {
        /**
         * fValue : -21.7467
         * sYearMonth : 2018-03
         */

        private double fValue;
        private String sYearMonth;
    }

    /**
     * 行业平均值
     */
    public static class VtDateValueAvgBean {
        /**
         * fValue : 7.50136
         * sYearMonth : 2016-12
         */

        private double fValue;
        private String sYearMonth;
    }

2.3.1 BarChart 的使用流程

使用流程如下

得到BarChart对象 并初始化
得到BarEntry对象,此处添加(X,Y)值
得到BarDataSet对象,添加BarEntry对象
得到BarData对象,添加BarDaraSet对象
显示柱状图 BarChart.setData(BarData)

BarChart与折线图LineChart很类似,基本会使用到如下属性

private BarChart barChart;
private YAxis leftAxis;             //左侧Y轴
private YAxis rightAxis;            //右侧Y轴
private XAxis xAxis;                //X轴
private Legend legend;              //图例
private LimitLine limitLine;        //限制线

然后进行相应的设置

    /**
 * 初始化BarChart图表
 */
private void initBarChart(BarChart barChart) {
    /***图表设置***/
    //背景颜色
    barChart.setBackgroundColor(Color.WHITE);
    //不显示图表网格
    barChart.setDrawGridBackground(false);
    //背景阴影
    barChart.setDrawBarShadow(false);
    barChart.setHighlightFullBarEnabled(false);
    //显示边框
    barChart.setDrawBorders(true);
    //设置动画效果
    barChart.animateY(1000, Easing.Linear);
    barChart.animateX(1000, Easing.Linear);

    /***XY轴的设置***/
    //X轴设置显示位置在底部
    xAxis = barChart.getXAxis();
    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
    xAxis.setAxisMinimum(0f);
    xAxis.setGranularity(1f);

    leftAxis = barChart.getAxisLeft();
    rightAxis = barChart.getAxisRight();
    //保证Y轴从0开始,不然会上移一点
    leftAxis.setAxisMinimum(0f);
    rightAxis.setAxisMinimum(0f);

    /***折线图例 标签 设置***/
    legend = barChart.getLegend();
    legend.setForm(Legend.LegendForm.LINE);
    legend.setTextSize(11f);
    //显示位置
    legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
    legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
    legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
    //是否绘制在图表里面
    legend.setDrawInside(false);
}

2.3.2 BarDataSet 初始化设置

BarDataSet与LineDataSet类似,一个BarDataSet对象就是一类柱状图,多列柱状图就是多个BarDataSet对象

    /**
 * 柱状图始化设置 一个BarDataSet 代表一列柱状图
 *
 * @param barDataSet 柱状图
 * @param color      柱状图颜色
 */
private void initBarDataSet(BarDataSet barDataSet, int color) {
    barDataSet.setColor(color);
    barDataSet.setFormLineWidth(1f);
    barDataSet.setFormSize(15.f);
    //显示柱状图顶部值
    barDataSet.setDrawValues(true);
   //barDataSet.setValueTextSize(10f);
  //barDataSet.setValueTextColor(color);
}

2.3.3 柱状图展示

此处先展示 公司的净资产收益率

    public void showBarChart(List<VtDateValueBean> dateValueList, String name, int color) {
    ArrayList<BarEntry> entries = new ArrayList<>();
    for (int i = 0; i < dateValueList.size(); i++) {
        /**
         * 此处还可传入Drawable对象 BarEntry(float x, float y, Drawable icon)
         * 即可设置柱状图顶部的 icon展示
         */
        BarEntry barEntry = new BarEntry(i, (float) dateValueList.get(i).getFValue());
        entries.add(barEntry);
    }
    // 每一个BarDataSet代表一类柱状图
    BarDataSet barDataSet = new BarDataSet(entries, name);
    initBarDataSet(barDataSet, color);

    BarData data = new BarData(barDataSet);
    barChart.setData(data);
}

然后在Activity中调用

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_bar_chart);

    barChart = findViewById(R.id.bar_chart);
    initBarChart(barChart);
    BarChartBean barChartBean = LocalJsonAnalyzeUtil.JsonToObject(this,
            "bar_chart.json", BarChartBean.class);
    List<VtDateValueBean> dateValueList = barChartBean.getStFinDate().getVtDateValue();
    Collections.reverse(dateValueList);//将集合 逆序排列,转换成需要的顺序

    showBarChart(dateValueList, "净资产收益率(%)", getResources().getColor(R.color.blue));
}

注意:不了解数据怎么来的可查看 相关文章:Android访问assets本地Json文件 或者本文相关代码

此时的图形效果

3. 柱状图外观完善

上图与需要实现的效果差距还是挺大的,下面待改善的点,然后挨着就行修改

去掉图表外框,以及右下角描述内容
去掉X Y轴实线,以及左侧Y轴
X轴改为日期显示
Y轴网格线改为虚线
Y轴值改为百分比,且间隔值为10%
靠近Y轴的柱状图未展示完整
负数值的柱状图未展示完整

3.1 去掉图表外框,描述内容以及X Y轴线条

不显示图表边框

barChart.setDrawBorders(false);

不显示右下角描述内容

Description description = new Description();
description.setEnabled(false);
barChart.setDescription(description);

不显示X轴 Y轴线条

xAxis.setDrawAxisLine(false);
leftAxis.setDrawAxisLine(false);
rightAxis.setDrawAxisLine(false);

不显示左侧Y轴

 leftAxis.setEnabled(false);
3.2 修改X Y轴网格线

虽然我们设置了不显示与图表网格线

barChart.setDrawGridBackground(false);

但是因为X Y轴还有自己的网格线,所以看起来图表的网格线仍然存在,所以还需对X轴Y轴的网格线进行设置

 //不显示X轴网格线
 xAxis.setDrawGridLines(false);
 //右侧Y轴网格线设置为虚线
 rightAxis.enableGridDashedLine(10f, 10f, 0f);
3.3 X Y轴自定义显示值

在 showBarChart 方法中我们会传入X轴的值,所以自定义X轴的值可以 写在该方法内

   //X轴自定义值
    xAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            return dateValueList.get((int) value % dateValueList.size()).getTradeDate();
        }
    });
    //右侧Y轴自定义值
    rightAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            return (int) value + "%";
        }
    });
3.4 柱状图显示负值

在这里柱状图未显示完整,完全是我自己的问题,如果在 initBarChart(BarChart barChart) 方法中不设置X轴Y轴,柱状图默认情况下是能显示完整的。

只需要在前面的基础上 删除 以下代码即可完整展示。

 xAxis.setAxisMinimum(0f);
 //保证Y轴从0开始,不然会上移一点
 leftAxis.setAxisMinimum(0f);
 rightAxis.setAxisMinimum(0f);

但前面为什么会写上那三行代码呢,因为在LineCahrt中,想要保证X Y 轴完全以 0 点开始。而柱状图的因为柱子宽度的原因,X轴的0值不需要 完全靠近 原点(0,0)
之后如图:


4.png
3.5 单条柱状图宽度

BarData类有设置柱状图宽度的方法,查看源码可以发现,传入的值 mBarWidth不是 px 以像素为单位,而是一个百分百值,默认值为0.85f 相当就是85%

宽度计算方式就是 BarChart控件宽度 / 柱状图数量 * mBarWidth ,以下为 设置值为 1 与 0.5 的效果

   BarData data = new BarData(barDataSet);
   data.setBarWidth(1f);
    barChart.setData(data);
1f.png
    BarData data = new BarData(barDataSet);
    data.setBarWidth(0.5f);
    barChart.setData(data);
0.5f.png
4. 多条柱状图
4.1 多条柱状图展示

多条柱状图,多添加一个BarDataSet对象即可。

/**
 * @param xValues   X轴的值
 * @param dataLists LinkedHashMap<String, List<Float>>
 *                  key对应柱状图名字  List<Float> 对应每类柱状图的Y值
 * @param colors
 */
public void showBarChart(final List<String> xValues, LinkedHashMap<String, List<Float>> dataLists,
                         @ColorRes List<Integer> colors) {

    List<IBarDataSet> dataSets = new ArrayList<>();
    int currentPosition = 0;//用于柱状图颜色集合的index

    for (LinkedHashMap.Entry<String, List<Float>> entry : dataLists.entrySet()) {
        String name = entry.getKey();
        List<Float> yValueList = entry.getValue();

        List<BarEntry> entries = new ArrayList<>();

        for (int i = 0; i < yValueList.size(); i++) {
            entries.add(new BarEntry(i, yValueList.get(i)));
        }
        // 每一个BarDataSet代表一类柱状图
        BarDataSet barDataSet = new BarDataSet(entries, name);
        initBarDataSet(barDataSet, colors.get(currentPosition));
        dataSets.add(barDataSet);

        currentPosition++;
    }

    //X轴自定义值
    xAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            return xValues.get((int) value % xValues.size());
        }
    });
    //右侧Y轴自定义值
    rightAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            return (int) value + "%";
        }
    });

    BarData data = new BarData(dataSets);
    barChart.setData(data);
}

在Activity中调用

    //处理数据是 记得判断每条柱状图对应的数据集合 长度是否一致
    LinkedHashMap<String, List<Float>> chartDataMap = new LinkedHashMap<>();
    List<String> xValues = new ArrayList<>();
    List<Float> yValue1 = new ArrayList<>();
    List<Float> yValue2 = new ArrayList<>();
    List<Integer> colors = Arrays.asList(
            getResources().getColor(R.color.blue), getResources().getColor(R.color.orange)
    );

    List<CompanyBean> companyBeans = lineChartBean.getGRID0().getResult().getCompany();
    List<CompositeIndexBean> compositeIndexBeans = lineChartBean.getGRID0().getResult().getCompositeIndexGEM();

    for (CompanyBean valueBean : companyBeans) {
        xValues.add(valueBean.getTradeDate());
        yValue1.add(Float.valueOf(valueBean.getRate()));
    }
    for (CompositeIndexBean compositeIndexBean : compositeIndexBeans) {
        yValue2.add(Float.valueOf(compositeIndexBean.getRate()*100));
    }
    chartDataMap.put("净资产收益率(%)", yValue1);
    chartDataMap.put("行业平均值(%)", yValue2);

    barChartManager.showBarChart(xValues, chartDataMap, colors);
5.png

4.2 由堆积柱状图变为并排多列柱状图
默认情况下,添加多条柱状图得到的效果是堆积柱状图,而现在需要的是 分组并列多条柱状图,所以还需要进行相应的设置。

柱状图分组设置

    BarData data = new BarData(dataSets);
   /**
    * 并排多列柱状图
    * float groupSpace = 0.3f;   //柱状图组之间的间距
    * float barSpace =  0.05f;  //每条柱状图之间的间距  一组两个柱状图
    * float barWidth = 0.3f;    //每条柱状图的宽度     一组两个柱状图
    * (barWidth + barSpace) * barAmount + groupSpace = (0.3 + 0.05) * 2 + 0.3 = 1.00
    * 3个数值 加起来 必须等于 1 即100% 按照百分比来计算 组间距 柱状图间距 柱状图宽度
    */
   int barAmount = dataLists.size(); //需要显示柱状图的类别 数量
   //设置组间距占比30% 每条柱状图宽度占比 70% /barAmount  柱状图间距占比 0%
    float groupSpace = 0.3f; //柱状图组之间的间距
    float barWidth = (1f - groupSpace) / barAmount;
    float barSpace = 0f;
    //设置柱状图宽度
    data.setBarWidth(barWidth);
    //(起始点、柱状图组间距、柱状图之间间距)
    data.groupBars(0f, groupSpace, barSpace);
    barChart.setData(data);
}

以上代码注意这个算式:

(barWidth + barSpace) * barAmount + groupSpace = (0.3 + 0.05) * 2 + 0.3 = 1.00

柱状图宽度 间距 值设多少看需求来定,但最终必须根据以上公式算出来 等于 1

现在的效果图


6.png

右侧还有部分图表未展示出来,此时还需要对X轴进行相应的设置

     //不加上会x轴显示不全 
    xAxis.setAxisMaximum(xValues.size());
    //将X轴的值显示在中央
    xAxis.setCenterAxisLabels(true);

加上 xAxis.setCenterAxisLabels(true); 这行代码后,前面自定义X轴的显示值则数组越界异常。

看错误日志是 X轴值为 -1,所以将自定义X轴的显示值改为:

   //X轴自定义值
    xAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            //加上 xAxis.setCenterAxisLabels(true); 这行代码后,前面自定义X轴的显示值则数组越界异常。
            //看错误日志是 X轴值为 -1,所以将自定义X轴的显示值改为:
            return xValues.get((int) Math.abs(value) % xValues.size());
        }
    });

现在如图如示


7.png
5. BarChart触摸事件
5.1 柱状图点击事件响应

在平常使用中,用的最多的事件就是柱状图点击事件了。

   //点击事件
    barChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
        @Override
        public void onValueSelected(Entry e, Highlight h) {
            e.getX();       //X轴坐标 记得转 int
            e.getY();       //当前柱状图Y轴值
            e.getIcon();    //对应 BarEntry(float x, float y, Drawable icon)
            e.getData();    //对应 BarEntry(float x, float y, Object data)
            Toast.makeText(BarChartActivity.this, "y:"+e.getY(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onNothingSelected() {
        }
    });

回调函数中的Entry对应showBarChart方法中 添加的BarEntry,可以在添加的时候 传入一个自定义数据,如name

entries.add(new BarEntry(i, yValueList.get(i), name));
然后在柱状图选中事件回调函数中得到我们自定义的数据 进行相应的识别。

   @Override
   public void onValueSelected(Entry e, Highlight h) {
        e.getData();    //对应 BarEntry(float x, float y, Object data)
   }

通过e.getData()方法就可以得到我们前面设置的值,在此处为点击的柱状图名字,即哪一类柱状图,此方法很灵活。

如果单纯的想知道点击的是哪一类柱状图可以采用以下方法:

  @Override
  public void onValueSelected(Entry e, Highlight h) {
       /得到包含此柱状图的 数据集
       BarDataSet dataSets = (BarDataSet) barChart.getBarData().getDataSetForEntry(e);
       dataSets.getLabel();
       dataSets.getEntryCount();
       List<BarEntry> barEntries = dataSets.getValues();
   }

通过e.getX()方法可以得到点柱状图所对应的X轴数据,然后就可以知道X轴所对应的所以柱状图值

        @Override
        public void onValueSelected(Entry e, Highlight h) {
            for (IBarDataSet dataSet : barChart.getBarData().getDataSets()) {
                BarEntry entry = dataSet.getEntryForIndex((int) e.getX());
            }
        }

5.2 禁用图表触摸事件

barChart.setDoubleTapToZoomEnabled(false);
//禁止拖拽
barChart.setDragEnabled(false);
//X轴或Y轴禁止缩放
barChart.setScaleXEnabled(false);
barChart.setScaleYEnabled(false);
barChart.setScaleEnabled(false);
//禁止所有事件
barChart.setTouchEnabled(false);

本文相关代码:https://gitee.com/zyd_gitee/android-mp-charts.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,784评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,745评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,702评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,229评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,245评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,376评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,798评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,471评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,655评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,485评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,535评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,235评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,793评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,863评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,096评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,654评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,233评论 2 341

推荐阅读更多精彩内容