MPAndroidChart在github上地址:https://github.com/PhilJay/MPAndroidChart
目标效果图为某公司的净资产收益率:
1. 数据准备
1.1 数据来源
数据是抓包佣金宝的数据,将获取的数据存入.json文件。
https://gitee.com/zyd_gitee/android-mp-charts/blob/master/app/src/main/assets/line_chart.json
2. 图表显示
2.1 MPAndroidChart获取
Github 地址:https://github.com/PhilJay/MPAndroidChart
依赖:
Project 的build.gradle文件中添加
然后在Project 的settings.gradle 中添加
然后在 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)
之后如图:
3.5 单条柱状图宽度
BarData类有设置柱状图宽度的方法,查看源码可以发现,传入的值 mBarWidth不是 px 以像素为单位,而是一个百分百值,默认值为0.85f 相当就是85%
宽度计算方式就是 BarChart控件宽度 / 柱状图数量 * mBarWidth ,以下为 设置值为 1 与 0.5 的效果
BarData data = new BarData(barDataSet);
data.setBarWidth(1f);
barChart.setData(data);
BarData data = new BarData(barDataSet);
data.setBarWidth(0.5f);
barChart.setData(data);
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);
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
现在的效果图
右侧还有部分图表未展示出来,此时还需要对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());
}
});
现在如图如示
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);