目录
前言
1. 数据准备
1.1 数据来源
2. 图表展示
2.1 MPAndroidChart获取
2.2 数据对象获取
2.3 数据展示
3. 柱状图外观完善
3.1 去掉图表外框,描述内容以及X Y轴线条
3.2 修改X Y轴网格线
3.3 X Y轴自定义显示值
3.4 柱状图显示不完整问题解决
3.5 单条柱状图宽度
4. 多条柱状图
4.1 多条柱状图展示
4.2 由堆积柱状图变为并排多列柱状图
5. BarChart触摸事件
5.1 柱状图点击事件响应
5.2 禁用图表触摸事件
6. 最终效果图
发现最新的MPAndroidChart和以前版本的使用有一些差距,就写下了现在新版的使用方法
注:博客2018/08/24更新 使用的MPAndroidChart版本3.0.3
相关文章:
Android图表控件MPAndroidChart的简单介绍(MPAndroidChart3.0)
Android图表控件MPAndroidChart——曲线图LineChart的使用(多条曲线)
Android图表控件MPAndroidChart——曲线图LineChart(多条曲线)动态添加数据
Android图表控件MPAndroidChart——柱状图BarChart的使用(多条柱状图)
Android图表控件MPAndroidChart——曲线图+柱状图 CombinedChart的使用
Android图表控件MPAndroidChart——源码修改实现曲线图X轴直尺刻度样式
本文相关代码
MPAndroidChart在github上地址:https://github.com/PhilJay/MPAndroidChart
目标效果图为中兴通讯的净资产收益率:
数据是抓包佣金宝的数据,将获取的数据存入.json文件。
json格式如下
Json 文件地址:
https://github.com/897532167/ChartManager/blob/master/app/src/main/assets/line_chart.json
Github 地址:https://github.com/PhilJay/MPAndroidChart
依赖:
Project 的build.gradle文件中添加
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
然后在 module中的build,gradle 中添加
dependencies {
implementation 'com.github.PhilJay:MPAndroidChart:v3.0.3'
}
然而大多情况下,我们会根据自己的需求自定义MPAndroidChart库,则需要下载源码并将MPChartLib引入自己的项目中。
在Android Studio app项目src同级目录下新建中新建assets文件夹,然后将步骤1.1得到的.json文件放入改文件夹中。
然后在获取.json文件中的json字符串并解析为对应的数据对象,可参考以下文章
相关文章: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 的使用流程
使用流程如下
2.3.2 BarChart 的初始化设置
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(false);
// barDataSet.setValueTextSize(10f);
// barDataSet.setValueTextColor(color);
}
2.3.3 柱状图展示
此处先展示 公司的净资产收益率
public void showBarChart(List dateValueList, String name, int color) {
ArrayList 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);
// // 添加多个BarDataSet时
// ArrayList dataSets = new ArrayList<>();
// dataSets.add(barDataSet);
// BarData data = new BarData(dataSets);
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 dateValueList = barChartBean.getStFinDate().getVtDateValue();
Collections.reverse(dateValueList);//将集合 逆序排列,转换成需要的顺序
showBarChart(dateValueList, "净资产收益率(%)", getResources().getColor(R.color.blue));
}
注意:不了解数据怎么来的可查看 相关文章:Android访问assets本地Json文件 或者本文相关代码
此时的图形效果
上图与需要实现的效果差距还是挺大的,下面待改善的点,然后挨着就行修改
不显示图表边框
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);
虽然我们设置了不显示与图表网格线
barChart.setDrawGridBackground(false);
但是因为X Y轴还有自己的网格线,所以看起来图表的网格线仍然存在,所以还需对X轴Y轴的网格线进行设置
//不显示X轴网格线
xAxis.setDrawGridLines(false);
//右侧Y轴网格线设置为虚线
rightAxis.enableGridDashedLine(10f, 10f, 0f);
在 showBarChart 方法中我们会传入X轴的值,所以自定义X轴的值可以 写在该方法内
//X轴自定义值
xAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return dateValueList.get((int) value % dateValueList.size()).getSYearMonth();
}
});
//右侧Y轴自定义值
rightAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return ((int) (value * 100)) + "%";
}
});
此时的图表效果
在这里柱状图未显示完整,完全是我自己的问题,如果在 initBarChart(BarChart barChart) 方法中不设置X轴Y轴,柱状图默认情况下是能显示完整的。
只需要在前面的基础上 删除 以下代码即可完整展示。
xAxis.setAxisMinimum(0f);
//保证Y轴从0开始,不然会上移一点
leftAxis.setAxisMinimum(0f);
rightAxis.setAxisMinimum(0f);
但前面为什么会写上那三行代码呢,因为在LineCahrt中,想要保证X Y 轴完全以 0 点开始。而柱状图的因为柱子宽度的原因,X轴的0值不需要 完全靠近 原点(0,0)
当前效果图如下
BarData类有设置柱状图宽度的方法,查看源码
可以发现,传入的值 mBarWidth不是 px 以像素为单位,而是一个百分百值,默认值为0.85f 相当就是85%
宽度计算方式就是 BarChart控件宽度 / 柱状图数量 * mBarWidth ,以下为 设置值为 1 与 0.5 的效果
BarData data = new BarData(barDataSet);
data.setBarWidth(1f);
多条柱状图,只是多添加一个BarDataSet对象即可。
/**
* @param xValues X轴的值
* @param dataLists LinkedHashMap>
* key对应柱状图名字 List 对应每类柱状图的Y值
* @param colors
*/
public void showBarChart(final List xValues, LinkedHashMap> dataLists,
@ColorRes List colors) {
List dataSets = new ArrayList<>();
int currentPosition = 0;//用于柱状图颜色集合的index
for (LinkedHashMap.Entry> entry : dataLists.entrySet()) {
String name = entry.getKey();
List yValueList = entry.getValue();
List 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 IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return xValues.get((int) value % xValues.size());
}
});
//右侧Y轴自定义值
rightAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return (int) (value) + "%";
}
});
BarData data = new BarData(dataSets);
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);
//处理数据是 记得判断每条柱状图对应的数据集合 长度是否一致
LinkedHashMap> chartDataMap = new LinkedHashMap<>();
List xValues = new ArrayList<>();
List yValue1 = new ArrayList<>();
List yValue2 = new ArrayList<>();
List colors = Arrays.asList(
getResources().getColor(R.color.blue), getResources().getColor(R.color.orange)
);
List valueList = barChartBean.getStFinDate().getVtDateValue();
List avgValueList = barChartBean.getStFinDate().getVtDateValueAvg();
Collections.reverse(valueList);
for (VtDateValueBean valueBean : valueList) {
xValues.add(valueBean.getSYearMonth());
yValue1.add((float) valueBean.getFValue());
}
for (VtDateValueAvgBean valueAvgBean : avgValueList) {
yValue2.add((float) valueAvgBean.getFValue());
}
chartDataMap.put("净资产收益率(%)", yValue1);
chartDataMap.put("行业平均值(%)", yValue2);
showBarChart(xValues, chartDataMap, colors);
}
此时的效果图
默认情况下,添加多条柱状图得到的效果是堆积柱状图,而现在需要的是 分组并列多条柱状图,所以还需要进行相应的设置。
柱状图分组设置
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轴进行相应的设置
xAxis.setAxisMinimum(0f);
xAxis.setAxisMaximum(xValues.size());
//将X轴的值显示在中央
xAxis.setCenterAxisLabels(true);
加上 xAxis.setCenterAxisLabels(true); 这行代码后,前面自定义X轴的显示值则数组越界异常。
看错误日志是 X轴值为 -1,所以将自定义X轴的显示值改为:
//X轴自定义值
xAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return xValues.get((int) Math.abs(value) % xValues.size());
}
});
此时就基本达到需求的效果图了
在平常使用中,用的最多的事件就是柱状图点击事件了。
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)
}
@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)
}
在此就相当于回复评论中的一个问题"setOnChartValueSelectedListener这个事件我只能获取到一组中点击的哪个但是点击的哪一组获取不到" 。
通过e.getData()方法就可以得到我们前面设置的值,在此处为点击的柱状图名字,即哪一类柱状图,此方法很灵活。
如果单纯的想知道点击的是哪一类柱状图可以采用以下方法:
@Override
public void onValueSelected(Entry e, Highlight h) {
//得到包含此柱状图的 数据集
BarDataSet dataSets = (BarDataSet) barChart.getBarData().getDataSetForEntry(e);
dataSets.getLabel();
dataSets.getEntryCount();
List 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());
}
}
barChart.setDoubleTapToZoomEnabled(false);
//禁止拖拽
barChart.setDragEnabled(false);
//X轴或Y轴禁止缩放
barChart.setScaleXEnabled(false);
barChart.setScaleYEnabled(false);
barChart.setScaleEnabled(false);
//禁止所有事件
barChart.setTouchEnabled(false);
BarChart的MarkerView的使用于LineChart类似,如有需要可查看:
Android图表控件MPAndroidChart——曲线图LineChart的使用(财富收益图)
本文相关代码:https://github.com/897532167/ChartManager