首先,引入 MPAndroidChart 依赖
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
xml代码
其次,在xml中使用MPAndroidChart库引用的实例:
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/left_titlebar"
android:background="@mipmap/bg_home_dh">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="ExtraText">
<lonan.net.guangzhou.ui.user.MyPieChart
android:id="@+id/pieChart"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_marginTop="@dimen/dp_20"
android:layout_below="@id/button36"/>
<com.github.mikephil.charting.charts.BarChart
android:id="@+id/barChart"
android:layout_width="match_parent"
android:layout_height="350dp"
android:layout_below="@id/pieChart" />
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
数据绑定
@BindView(R.id.pieChart)
MyPieChart pieChart;
@BindView(R.id.barChart)
BarChart barChart;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FullActivityUtils.full(this);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.demo);
MyApplication.addActivity(this);
titlebar2.getCenterTextView().setText("****");
//自己定义一个getData方法,方法执行结束后,执行图表渲染的方法,将数据绑定上去
getData();
}
图表代码
PieChart饼图和BarChart柱状图:
private void setPieChartData() {
for (int i = 0; i < itemNumber; i++) {
yValues.add(new PieEntry(itemValue.get(i), itemName.get(i)));
}
PieDataSet pieDataSet = new PieDataSet(yValues, "");
pieDataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);//value外部展示
PieData pieData = new PieData(pieDataSet);
pieChart.setData(pieData);
pieChart.setExtraOffsets(40f, 0f, 40f, 0f);
//设置各个数据的颜色
pieDataSet.setColors(ColorTemplate.COLORFUL_COLORS);
//实体扇形的空心圆的半径 设置成0时就是一个圆 而不是一个环
pieChart.setHoleRadius(30);
//中间半透明白色圆的半径 设置成0时就是隐藏
pieChart.setTransparentCircleRadius(30);
//设置中心圆的颜色
pieChart.setHoleColor(Color.CYAN);
//设置中心部分的字 (一般中间白色圆不隐藏的情况下才设置)
pieChart.setCenterText("定义在圆环中心的标题");
//设置中心字的字体颜色
pieChart.setCenterTextColor(Color.BLACK);
//设置描述的字体大小
pieChart.setEntryLabelTextSize(12);
pieChart.setEntryLabelColor(Color.WHITE);
Description description = pieChart.getDescription();
description.setText("定义你自己的标题");
//设置数据的字体大小
pieDataSet.setValueTextSize(12);
//设置描述的位置
pieDataSet.setXValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
pieDataSet.setValueLinePart1Length(1.2f);//设置描述连接线长度
//设置数据的位置
pieDataSet.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
pieDataSet.setValueLinePart2Length(1.2f);//设置数据连接线长度
//设置两根连接线的颜色
pieDataSet.setValueLineColor(Color.WHITE);
//对于右下角一串字母的操作
pieChart.getDescription().setEnabled(false); //是否显示右下角描述
//获取图例,是否显示图例
pieChart.getLegend().setEnabled(false);
pieChart.animateY(1000); //在Y轴的动画 参数是动画执行时间 毫秒为单位
}
private void setBarChartData() {
ArrayList<BarEntry> barEntries = new ArrayList<>();
for (int i = 0; i < itemNumber; i++) {
barEntries.add(new BarEntry(i, itemValue.get(i)));
}
BarDataSet barDataSet = new BarDataSet(barEntries, "");
barDataSet.setValueTextColor(Color.RED);
barDataSet.setValueTextSize(15f);
barDataSet.setColor(Color.parseColor("#03A9F4"));
BarData barData = new BarData(barDataSet);
barData.setBarWidth(0.3f);
//设置控件之间的间距
barChart.setExtraOffsets(20, 20, 20, 20);
//获取XAxis 获取XAxis setDrawGridLines:设置绘图网格线
barChart.getXAxis().setDrawGridLines(false);
//获取描述,是否显示右下角描述
barChart.getDescription().setEnabled(false);
//获取图例,是否显示图例
barChart.getLegend().setEnabled(false);
barChart.setData(barData);
barChart.getDescription().setText("定义你自己的标题");
barChart.getDescription().setEnabled(true);
XAxis xAxis = barChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setAxisMinimum(0f);
xAxis.setAxisMaximum(itemNumber);
xAxis.setLabelCount(itemNumber);
xAxis.setGranularity(1);
xAxis.setTextColor(Color.BLACK);
xAxis.setLabelRotationAngle(45f);
xAxis.setLabelCount(itemNumber, true);
xAxis.setValueFormatter(new ValueFormatter() {
@Override
public String getFormattedValue(float value) {
int index = (int) value;
if (index >= 0 && index < itemNumber) {
return itemName.get(index);
} else {
return "";
}
}
});
YAxis axisRight = barChart.getAxisRight();
axisRight.setEnabled(false);
YAxis axisLeft = barChart.getAxisLeft();
axisLeft.setTextColor(Color.BLACK);
axisLeft.setAxisMaximum(250f); // 设置适当的最大值
axisLeft.setAxisMinimum(0f);
axisLeft.setTextSize(15f);
barChart.animateXY(1000, 1000);
barChart.invalidate();
}
使用中遇到的问题
对图表的样式、大小,渲染效果需要调整的可以考虑重写库的渲染方法。比如,我遇到的有一个问题,数据太多但是MPAndroidChart 饼状图太小,出现了文字重叠问题,以下提供一个解决问题的思路:可以尝试根据自己的需求重写PieChartRenderer。
public class MyPieChart extends PieChart {
public MyPieChart(Context context) {
super(context);
}
public MyPieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyPieChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
//此处把mRenderer替换成我们自己的PieChartRenderer
mRenderer = new PieChartCustomRendederer(this, mAnimator, mViewPortHandler);
}
}
public class PieChartCustomRendederer extends PieChartRenderer {
private Paint mEntryLabelsPaint = new Paint();
public PieChartCustomRendederer(PieChart chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
private float[] minData(float leftRecordY[][], float pt1y) {
//计算出离的最近的
List<Float> bigD = new ArrayList<>();
//变换前的数据
List<Float> nearestlist = new ArrayList<>();
//变换后的数据
List<Float> nearestlistCopy = new ArrayList<>();
for (int k = 0; k < leftRecordY[0].length; k++) {
if (leftRecordY[0][k] != 0) {
bigD.add(Math.abs(leftRecordY[0][k] - pt1y));
nearestlist.add(leftRecordY[0][k]);
nearestlistCopy.add(leftRecordY[1][k]);
}
}
// 距离最近的点,数值
float[] rF = new float[2];
if (bigD.size() == 0) {
return rF;
}
float minD = bigD.get(0);
rF[0] = nearestlist.get(0);
rF[1] = nearestlistCopy.get(0);
for (int g = 0; g < bigD.size(); g++) {
if (bigD.get(g) < minD) {
minD = bigD.get(g);
rF[0] = nearestlist.get(g);
rF[1] = nearestlistCopy.get(g);
}
}
return rF;
}
private String text = "2.0%";
@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 roundedRadius = (radius - (radius * mChart.getHoleRadius() / 100f)) / 2f;
final float holeRadiusPercent = mChart.getHoleRadius() / 100.f;
float labelRadiusOffset = radius / 10f * 3.6f;
if (mChart.isDrawHoleEnabled()) {
labelRadiusOffset = (radius - (radius * holeRadiusPercent)) / 2f;
if (!mChart.isDrawSlicesUnderHoleEnabled() && mChart.isDrawRoundedSlicesEnabled()) {
// Add curved circle slice and spacing to rotation angle, so that it sits nicely inside
rotationAngle += roundedRadius * 360 / (Math.PI * 2 * radius);
}
}
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 = com.github.mikephil.charting.utils.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);
Rect rect = new Rect();
mValuePaint.getTextBounds(text, 0, text.length(), rect);
mValuePaint.setColor(dataSet.getColor(i));
int textHeight = (int) (rect.height() * 1.4f);//文本的高度
float lineHeight = Utils.calcTextHeight(mValuePaint, "Q") + Utils.convertDpToPixel(4f);
ValueFormatter formatter = dataSet.getValueFormatter();
int entryCount = dataSet.getEntryCount();
mValueLinePaint.setColor(dataSet.getValueLineColor());
mValueLinePaint.setStrokeWidth(com.github.mikephil.charting.utils.Utils.convertDpToPixel(dataSet.getValueLineWidth()));
final float sliceSpace = getSliceSpace(dataSet);
MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
iconsOffset.x = com.github.mikephil.charting.utils.Utils.convertDpToPixel(iconsOffset.x);
iconsOffset.y = com.github.mikephil.charting.utils.Utils.convertDpToPixel(iconsOffset.y);
//第一个存以前的,第二个存变换位置后的
float leftRecordY[][] = new float[2][entryCount];
float rightRecordY[][] = new float[2][entryCount];
for (int j = 0; j < entryCount; j++) {
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 / (com.github.mikephil.charting.utils.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();
String formattedValue = formatter.getPieLabel(value, entry);
String entryLabel = entry.getLabel();
final float sliceXBase = (float) Math.cos(transformedAngle * com.github.mikephil.charting.utils.Utils.FDEG2RAD);
final float sliceYBase = (float) Math.sin(transformedAngle * com.github.mikephil.charting.utils.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;
float pt0x = line1Radius * sliceXBase + center.x;
float pt0y = line1Radius * sliceYBase + center.y;
float pt1x = labelRadius * (1 + valueLineLength1) * sliceXBase + center.x;
float pt1y = labelRadius * (1 + valueLineLength1) * sliceYBase + center.y;
if (transformedAngle % 360.0 >= 90.0 && transformedAngle % 360.0 <= 270.0) {
float nearestPoint[] = minData(leftRecordY, pt1y);
leftRecordY[0][j] = pt1y;
//判断是否需要挪位置
if (nearestPoint[0] != 0 && Math.abs(nearestPoint[0] - pt1y) < (textHeight + lineHeight)) {
pt1y = nearestPoint[1] - textHeight;
}
pt2x = pt1x - polyline2Width;
pt2y = pt1y;
mValuePaint.setTextAlign(Paint.Align.RIGHT);
if (drawXOutside)
mEntryLabelsPaint.setTextAlign(Paint.Align.RIGHT);
labelPtx = pt2x - offset;
labelPty = pt2y;
leftRecordY[1][j] = pt1y;
} else {
float[] nearestPoint = minData(rightRecordY, pt1y);
rightRecordY[0][j] = pt1y;
//判断是否需要挪位置
if (nearestPoint[0] != 0 && Math.abs(nearestPoint[0] - pt1y) < (textHeight + lineHeight)) {
pt1y = nearestPoint[1] + textHeight;
}
pt2x = pt1x + polyline2Width;
pt2y = pt1y;
mValuePaint.setTextAlign(Paint.Align.LEFT);
if (drawXOutside)
mEntryLabelsPaint.setTextAlign(Paint.Align.LEFT);
labelPtx = pt2x + offset;
labelPty = pt2y;
rightRecordY[1][j] = labelPty;
}
if (dataSet.getValueLineColor() != ColorTemplate.COLOR_NONE) {
if (dataSet.isUsingSliceColorAsValueLineColor()) {
mValueLinePaint.setColor(dataSet.getColor(j));
}
c.drawLine(pt0x, pt0y, pt1x, pt1y, mValueLinePaint);
c.drawLine(pt1x, pt1y, pt2x, pt2y, mValueLinePaint);
}
// draw everything, depending on settings
if (drawXOutside && drawYOutside) {
drawValue(c, formattedValue, labelPtx, labelPty, dataSet.getValueTextColor(j));
if (j < data.getEntryCount() && entryLabel != null) {
drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight);
}
} else if (drawXOutside) {
if (j < data.getEntryCount() && entryLabel != null) {
drawEntryLabel(c, entryLabel, labelPtx, labelPty + lineHeight / 2.f);
}
} else if (drawYOutside) {
drawValue(c, formattedValue, 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(Paint.Align.CENTER);
// draw everything, depending on settings
if (drawXInside && drawYInside) {
drawValue(c, formattedValue, x, y, dataSet.getValueTextColor(j));
if (j < data.getEntryCount() && entryLabel != null) {
drawEntryLabel(c, entryLabel, x, y + lineHeight);
}
} else if (drawXInside) {
if (j < data.getEntryCount() && entryLabel != null) {
drawEntryLabel(c, entryLabel, x, y + lineHeight / 2f);
}
} else if (drawYInside) {
drawValue(c, formattedValue, x, y + lineHeight / 2f, dataSet.getValueTextColor(j));
}
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
float x = (labelRadius + iconsOffset.y) * sliceXBase + center.x;
float y = (labelRadius + iconsOffset.y) * sliceYBase + center.y;
y += iconsOffset.x;
Utils.drawImage(
c,
icon,
(int) x,
(int) y,
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
xIndex++;
}
MPPointF.recycleInstance(iconsOffset);
}
MPPointF.recycleInstance(center);
c.restore();
}
}
总结
代码可以直接复制过去用,注意依赖引入的版本,版本不一致,代码可能有报错
有问题的同学欢迎留言
如果帮到你了,不妨点个赞