0x01 前言
MPAndroidChart的图标库非常强大,参考他自带的demo就可以实现我们的需求,这里介绍一些demo里没有或网上资料比较少的样例。(主要介绍饼状图和水平条形图)
先上样式图,github地址下载:MPChartDemo
0x02. 饼状图
有时候我们饼状图的需求要求我们画出的标签折线颜色和饼状图的颜色一致,但MPAndroidChart并没有提供我们更改标签折线颜色的接口,如何解决这样的问题?
我们可以重新封装一下PieChart、PieChartRenderer、PieHighlighter三个类。继承的类分别为MvPieChart、MvPieChartRenderer、MvPieHighlighter。
MvPieChart类中将PieChart代码copy过来,在初始化的时候修改mRenderer和mHighlighter对象初始化为我们封装的类
@Override
protected void init() {
super.init();
mRenderer = new MvPieChartRenderer(this, mAnimator, mViewPortHandler);
mXAxis = null;
mHighlighter = new MvPieHighlighter(this);
}
MvPieChartRenderer类中解决标签重叠和颜色问题
@Override
public void drawValues(Canvas c) {
MPPointF center = mChart.getCenterCircleBox();
// get whole the radius
float radius = mChart.getRadius();
float rotationAngle = mChart.getRotationAngle();
float[] drawAngles = mChart.getDrawAngles();
float[] absoluteAngles = mChart.getAbsoluteAngles();
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
final float holeRadiusPercent = mChart.getHoleRadius() / 100.f;
float labelRadiusOffset = radius / 10f * 3.6f;
if (mChart.isDrawHoleEnabled()) {
labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f;
}
final float labelRadius = radius - labelRadiusOffset;
PieData data = mChart.getData();
List<IPieDataSet> dataSets = data.getDataSets();
float yValueSum = data.getYValueSum();
boolean drawEntryLabels = mChart.isDrawEntryLabelsEnabled();
float angle;
int xIndex = 0;
c.save();
float offset = Utils.convertDpToPixel(5.f);
for (int i = 0; i < dataSets.size(); i++) {
IPieDataSet dataSet = dataSets.get(i);
final boolean drawValues = dataSet.isDrawValuesEnabled();
if (!drawValues && !drawEntryLabels)
continue;
final PieDataSet.ValuePosition xValuePosition = dataSet.getXValuePosition();
final PieDataSet.ValuePosition yValuePosition = dataSet.getYValuePosition();
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
float lineHeight = Utils.calcTextHeight(mValuePaint, "Q")
+ Utils.convertDpToPixel(4f);
IValueFormatter formatter = dataSet.getValueFormatter();
int entryCount = dataSet.getEntryCount();
mValueLinePaint.setColor(dataSet.getValueLineColor());
mValueLinePaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getValueLineWidth()));
final float sliceSpace = getSliceSpace(dataSet);
for (int j = 0; j < entryCount; j++) {
/***********解决线条颜色同步************/
int defaultValueLineColor = 0xff000000;
int setLineColor = defaultValueLineColor;
if (defaultValueLineColor == dataSet.getValueLineColor()) {
try {
setLineColor = data.getColors()[j % data.getColors().length];
} catch (Exception e) {
e.printStackTrace();
}
} else {
setLineColor = defaultValueLineColor;
}
mValueLinePaint.setColor(setLineColor);
/***********解决线条颜色同步************/
PieEntry entry = dataSet.getEntryForIndex(j);
if (xIndex == 0)
angle = 0.f;
else
angle = absoluteAngles[xIndex - 1] * phaseX;
final float sliceAngle = drawAngles[xIndex];
final float sliceSpaceMiddleAngle = sliceSpace / (Utils.FDEG2RAD * labelRadius);
// offset needed to center the drawn text in the slice
final float angleOffset = (sliceAngle - sliceSpaceMiddleAngle / 2.f) / 2.f;
angle = angle + angleOffset;
final float transformedAngle = rotationAngle + angle * phaseY;
float value = mChart.isUsePercentValuesEnabled() ? entry.getY()
/ yValueSum * 100f : entry.getY();
final float sliceXBase = (float) Math.cos(transformedAngle * Utils.FDEG2RAD);
final float sliceYBase = (float) Math.sin(transformedAngle * Utils.FDEG2RAD);
final boolean drawXOutside = drawEntryLabels &&
xValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE;
final boolean drawYOutside = drawValues &&
yValuePosition == PieDataSet.ValuePosition.OUTSIDE_SLICE;
final boolean drawXInside = drawEntryLabels &&
xValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE;
final boolean drawYInside = drawValues &&
yValuePosition == PieDataSet.ValuePosition.INSIDE_SLICE;
if (drawXOutside || drawYOutside) {
final float valueLineLength1 = dataSet.getValueLinePart1Length();
final float valueLineLength2 = dataSet.getValueLinePart2Length();
final float valueLinePart1OffsetPercentage = dataSet.getValueLinePart1OffsetPercentage() / 100.f;
float pt2x, pt2y;
float labelPtx, labelPty;
float line1Radius;
if (mChart.isDrawHoleEnabled())
line1Radius = (radius - (radius * holeRadiusPercent))
* valueLinePart1OffsetPercentage
+ (radius * holeRadiusPercent);
else
line1Radius = radius * valueLinePart1OffsetPercentage;
final float polyline2Width = dataSet.isValueLineVariableLength()
? labelRadius * valueLineLength2 * (float) Math.abs(Math.sin(
transformedAngle * Utils.FDEG2RAD))
: labelRadius * valueLineLength2;
final float pt0x = line1Radius * sliceXBase + center.x;
final float pt0y = line1Radius * sliceYBase + center.y;
final float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x;
final float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y;
if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) {
pt2x = pt1x - polyline2Width;
// pt2y = pt1y;
/***********解决标签重叠************/
if (j == 0) {
pt2y = pt1y;
} else {
pt2y = pt1y + dataSet.getValueTextSize() * j;
}
/***********解决标签重叠************/
mValuePaint.setTextAlign(Align.RIGHT);
if(drawXOutside)
mEntryLabelsPaint.setTextAlign(Align.RIGHT);
labelPtx = pt2x - offset;
labelPty = pt2y;
} else {
pt2x = pt1x + polyline2Width;
// pt2y = pt1y;
/***********解决标签重叠************/
if (j == 0) {
pt2y = pt1y;
} else {
pt2y = pt1y + dataSet.getValueTextSize() * j;
}
/***********解决标签重叠************/
mValuePaint.setTextAlign(Align.LEFT);
if(drawXOutside)
mEntryLabelsPaint.setTextAlign(Align.LEFT);
labelPtx = pt2x + offset;
labelPty = pt2y;
}
if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) {
c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint);
c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint);
}
// draw everything, depending on settings
if (drawXOutside && drawYOutside) {
drawValue(c,
formatter,
value,
entry,
0,
labelPtx,
labelPty,
dataSet.getValueTextColor(j));
if (j < data.getEntryCount() && entry.getLabel() != null) {
drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight);
}
} else if (drawXOutside) {
if (j < data.getEntryCount() && entry.getLabel() != null) {
drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f);
}
} else if (drawYOutside) {
/***********解决标签字体颜色同步************/
int valueColor = setLineColor;
if (dataSet.getValueLineColor() != Color.BLACK) {
valueColor = dataSet.getValueTextColor(j);
}
drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, valueColor);
/**********解决标签字体颜色同步***********/
// drawValue(c, formatter, value, entry, 0, labelPtx, labelPty + lineHeight / 2.f, dataSet
// .getValueTextColor(j));
}
}
if (drawXInside || drawYInside) {
// calculate the text position
float x = labelRadius * sliceXBase + center.x;
float y = labelRadius * sliceYBase + center.y;
mValuePaint.setTextAlign(Align.CENTER);
// draw everything, depending on settings
if (drawXInside && drawYInside) {
drawValue(c, formatter, value, entry, 0, x, y, dataSet.getValueTextColor(j));
if (j < data.getEntryCount() && entry.getLabel() != null) {
drawEntryLabel(c, entry.getLabel(), x, y + lineHeight);
}
} else if (drawXInside) {
if (j < data.getEntryCount() && entry.getLabel() != null) {
drawEntryLabel(c, entry.getLabel(), x, y + lineHeight / 2f);
}
} else if (drawYInside) {
drawValue(c, formatter, value, entry, 0, x, y + lineHeight / 2f, dataSet.getValueTextColor(j));
}
}
xIndex++;
}
}
MPPointF.recycleInstance(center);
c.restore();
}
MvPieHighlighter类的构造函数传入的是MvPieChart 对象
public MvPieHighlighter(MvPieChart chart) {
super(chart);
}
按以上方法封装即可解决标签重叠和标签颜色问题。标签文字的颜色可以通过提供的接口进行修改
Legend l = mChart.getLegend();
l.setTextColor(Color.parseColor("#ff9933"));
注意在xml文件中使用MvPieChart作为控件
<com.dhunter.mpchart.MpAndroidChart.MvPieChart
android:id="@+id/pieChart"
android:layout_width="match_parent"
android:layout_marginTop="24dp"
android:layout_height="330dp">
</com.dhunter.mpchart.MpAndroidChart.MvPieChart>
0x03 水平条形图
水平条形图为竖直条形图的x,y轴旋转。这里的示例是一个简单的水平条形图。一般情况下我们是通过网络获取数据,然后更新我们的图表,所以将图表的初始化和数据刷新分开。
初始化如下:
protected void initLayout() {
// 水平条形图初始化
ViewGroup.LayoutParams lp = mHorizontalBarChart.getLayoutParams();
lp.width = ScreenUtil.getWidth(this);
lp.height = ScreenUtil.getWidth(this);
mHorizontalBarChart.setLayoutParams(lp);
//设置相关属性
mHorizontalBarChart.setTouchEnabled(false);
mHorizontalBarChart.setDrawBarShadow(false);
mHorizontalBarChart.setDrawValueAboveBar(true);
mHorizontalBarChart.getDescription().setEnabled(false);
mHorizontalBarChart.setPinchZoom(false);
mHorizontalBarChart.setNoDataText("无数据");
mHorizontalBarChart.setDrawGridBackground(false);
//x轴
XAxis xAxis = mHorizontalBarChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setDrawAxisLine(false);
xAxis.setDrawGridLines(false);
xAxis.setAvoidFirstLastClipping(true);
xAxis.setDrawLabels(true);
xAxis.setGranularity(10f);
//y轴
YAxis yAxis = mHorizontalBarChart.getAxisLeft();
yAxis.setDrawAxisLine(false);
yAxis.setDrawGridLines(false);
yAxis.setAxisMinimum(0f);
yAxis.setEnabled(false);
//y轴
YAxis yr = mHorizontalBarChart.getAxisRight();
yr.setEnabled(false);
mHorizontalBarChart.setFitBars(true);
mHorizontalBarChart.animateY(2500);
Legend legend = mHorizontalBarChart.getLegend();
legend.setEnabled(false);
}
设置数据
protected void requestData() {
XAxis xAxis = mHorizontalBarChart.getXAxis();
xAxis.setLabelCount(10);
xAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return lable[(int) (value / spaceForBar)];
}
@Override
public int getDecimalDigits() {
return 0;
}
});
float barWidth = 8f;
ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>();
for (int i = 0; i < 10; i++) {
float val = new Random().nextInt(1000);
yVals1.add(new BarEntry(i * spaceForBar, val));
}
BarDataSet set1;
if (mHorizontalBarChart.getData() != null &&
mHorizontalBarChart.getData().getDataSetCount() > 0) {
set1 = (BarDataSet) mHorizontalBarChart.getData().getDataSetByIndex(0);
set1.setValues(yVals1);
mHorizontalBarChart.getData().notifyDataChanged();
mHorizontalBarChart.notifyDataSetChanged();
} else {
set1 = new BarDataSet(yVals1, "DataSet 1");
set1.setColor(0xffff7700);
set1.setDrawValues(true);
//显示为整数
set1.setValueFormatter(new IValueFormatter() {
@Override
public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
int valuedate = (int) value;
return valuedate + "";
}
});
ArrayList<IBarDataSet> dataSets = new ArrayList<IBarDataSet>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
data.setValueTextSize(10f);
data.setBarWidth(barWidth);
mHorizontalBarChart.setData(data);
}
}
0x04 附
附上部分接口的设置作用,大家有兴趣可以试试
/** 设置 pieChart 图表基本属性 **/
// mChart.setUsePercentValues(false); //使用百分比显示
// mChart.getDescription().setEnabled(false); //设置pieChart图表的描述
// mChart.setBackgroundColor(Color.YELLOW); //设置pieChart图表背景色
// mChart.setExtraOffsets(5, 10, 60, 10); //设置pieChart图表上下左右的偏移,类似于外边距
// mChart.setDragDecelerationFrictionCoef(0.95f);//设置pieChart图表转动阻力摩擦系数[0,1]
// mChart.setRotationAngle(0); //设置pieChart图表起始角度
// mChart.setRotationEnabled(true); //设置pieChart图表是否可以手动旋转
// mChart.setHighlightPerTapEnabled(true); //设置piecahrt图表点击Item高亮是否可用
// mChart.animateY(1400, Easing.EasingOption.EaseInOutQuad);// 设置pieChart图表展示动画效果
//
// // 设置 pieChart 图表Item文本属性
// mChart.setDrawEntryLabels(true); //设置pieChart是否只显示饼图上百分比不显示文字(true:下面属性才有效果)
// mChart.setEntryLabelColor(Color.WHITE); //设置pieChart图表文本字体颜色
// mChart.setEntryLabelTypeface(mTfRegular); //设置pieChart图表文本字体样式
// mChart.setEntryLabelTextSize(10f); //设置pieChart图表文本字体大小
//
// // 设置 pieChart 内部圆环属性
// mChart.setDrawHoleEnabled(true); //是否显示PieChart内部圆环(true:下面属性才有意义)
// mChart.setHoleRadius(28f); //设置PieChart内部圆的半径(这里设置28.0f)
// mChart.setTransparentCircleRadius(31f); //设置PieChart内部透明圆的半径(这里设置31.0f)
// mChart.setTransparentCircleColor(Color.BLACK);//设置PieChart内部透明圆与内部圆间距(31f-28f)填充颜色
// mChart.setTransparentCircleAlpha(50); //设置PieChart内部透明圆与内部圆间距(31f-28f)透明度[0~255]数值越小越透明
// mChart.setHoleColor(Color.WHITE); //设置PieChart内部圆的颜色
// mChart.setDrawCenterText(true); //是否绘制PieChart内部中心文本(true:下面属性才有意义)
// mChart.setCenterTextTypeface(mTfLight); //设置PieChart内部圆文字的字体样式
// mChart.setCenterText("Test"); //设置PieChart内部圆文字的内容
// mChart.setCenterTextSize(10f); //设置PieChart内部圆文字的大小
// mChart.setCenterTextColor(Color.RED); //设置PieChart内部圆文字的颜色
//
// // pieChart添加数据
// setData();
//
// // 获取pieCahrt图列
// Legend l = mChart.getLegend();
// l.setEnabled(true); //是否启用图列(true:下面属性才有意义)
// l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
// l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.RIGHT);
// l.setOrientation(Legend.LegendOrientation.VERTICAL);
// l.setForm(Legend.LegendForm.DEFAULT); //设置图例的形状
// l.setFormSize(10); //设置图例的大小
// l.setFormToTextSpace(10f); //设置每个图例实体中标签和形状之间的间距
// l.setDrawInside(false);
// l.setWordWrapEnabled(true); //设置图列换行(注意使用影响性能,仅适用legend位于图表下面)
// l.setXEntrySpace(10f); //设置图例实体之间延X轴的间距(setOrientation = HORIZONTAL有效)
// l.setYEntrySpace(8f); //设置图例实体之间延Y轴的间距(setOrientation = VERTICAL 有效)
// l.setYOffset(0f); //设置比例块Y轴偏移量
// l.setTextSize(14f); //设置图例标签文本的大小
// l.setTextColor(Color.parseColor("#ff9933"));//设置图例标签文本的颜色
// * 设置饼图的数据
// */
//private void setData() {
// ArrayList<PieEntry> pieEntryList = new ArrayList<PieEntry>();
// ArrayList<Integer> colors = new ArrayList<Integer>();
// colors.add(Color.parseColor("#f17548"));
// colors.add(Color.parseColor("#FF9933"));
// //饼图实体 PieEntry
// PieEntry CashBalance = new PieEntry(70, "现金余额 1500");
// PieEntry ConsumptionBalance = new PieEntry(30, "消费余额 768");
// pieEntryList.add(CashBalance);
// pieEntryList.add(ConsumptionBalance);
// //饼状图数据集 PieDataSet
// PieDataSet pieDataSet = new PieDataSet(pieEntryList, "资产总览");
// pieDataSet.setSliceSpace(3f); //设置饼状Item之间的间隙
// pieDataSet.setSelectionShift(10f); //设置饼状Item被选中时变化的距离
// pieDataSet.setColors(colors); //为DataSet中的数据匹配上颜色集(饼图Item颜色)
// //最终数据 PieData
// PieData pieData = new PieData(pieDataSet);
// pieData.setDrawValues(true); //设置是否显示数据实体(百分比,true:以下属性才有意义)
// pieData.setValueTextColor(Color.BLUE); //设置所有DataSet内数据实体(百分比)的文本颜色
// pieData.setValueTextSize(12f); //设置所有DataSet内数据实体(百分比)的文本字体大小
// pieData.setValueTypeface(mTfLight); //设置所有DataSet内数据实体(百分比)的文本字体样式
// pieData.setValueFormatter(new PercentFormatter());//设置所有DataSet内数据实体(百分比)的文本字体格式
// mChart.setData(pieData);
// mChart.highlightValues(null);
// mChart.invalidate(); //将图表重绘以显示设置的属性和数据
// }