android小功能的实现Android 报表Android知识

MPAndroidChart项目实战(三)——饼状图实现和文字重

2017-04-18  本文已影响265人  天一方蓝

我的CSDN:http://blog.csdn.net/dt235201314/article/details/70142117
源码下载(UpDating 欢迎Star):
https://github.com/JinBoy23520/MPAndroidChartDemoByJin
一丶概述
上一篇代码补了这么久,不好意思,今天再说说MPAndroidChart实现饼状图以及文字冲突问题解决。
二丶演示效果

三丶实现功能
1.饼状图实现
2.解决当占比过小,文字重合问题

四丶看代码
与上一篇,提高代码复用率
PieChartEntity.Java 设置基本属性(这里不做详细说明,百度有文章可查看属性)

/**
 * 饼状图
 * Created by jin
 */
public class PieChartEntity  {
    private PieChart mChart;
    private List<PieEntry> mEntries;
    private String[] labels;
    private int[] mPieColors;
    private int mValueColor;
    private float mTextSize;
    private PieDataSet.ValuePosition mValuePosition;

    public PieChartEntity(PieChart chart, List<PieEntry> entries, String[] labels,
                          int []chartColor,  float textSize, int valueColor, PieDataSet.ValuePosition valuePosition) {
        this.mChart = chart;
        this.mEntries = entries;
        this.labels= labels;
        this.mPieColors = chartColor;
        this.mTextSize= textSize;
        this.mValueColor = valueColor;
        this.mValuePosition = valuePosition;
        initPieChart();
    }

    public PieChartEntity(PieChart chart, List<PieEntry> entries, String[] labels,
                          int []chartColor,  float textSize, int valueColor) {
        this(chart, entries, labels, chartColor, textSize, valueColor, PieDataSet.ValuePosition.INSIDE_SLICE);

    }

    private void initPieChart() {
        mChart.setExtraOffsets(5, 10, 5, 5);

        mChart.setDragDecelerationFrictionCoef(0.95f);
        mChart.setDrawCenterText(false);
        mChart.getDescription().setEnabled(false);
        mChart.setRotationAngle(0);
        // enable rotation of the chart by touch
        mChart.setRotationEnabled(true);
        mChart.setHighlightPerTapEnabled(true);
        mChart.setDrawEntryLabels(true);
        setChartData();
        mChart.animateY(1000, Easing.EasingOption.EaseInOutQuad);

        Legend l = mChart.getLegend();
        l.setVerticalAlignment(Legend.LegendVerticalAlignment.TOP);
        l.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
        l.setOrientation(Legend.LegendOrientation.VERTICAL);
        l.setDrawInside(false);
        l.setXEntrySpace(7f);
        l.setYEntrySpace(1f);
        l.setYOffset(0f);
        // entry label styling
        mChart.setEntryLabelColor(mValueColor);
        mChart.setEntryLabelTextSize(mTextSize);
        mChart.setExtraOffsets(10, 10, 10, 10);
    }

    public void setHoleDisenabled () {
        mChart.setDrawHoleEnabled(false);
    }

    /**
     * 中心圆是否可见
     * @param holeColor 中心圆颜色
     * @param holeRadius 半径
     * @param transColor 透明圆颜色
     * @param transRadius 透明圆半径
     */
    public void setHoleEnabled (int holeColor, float holeRadius, int transColor, float transRadius) {
        mChart.setDrawHoleEnabled(true);
        mChart.setHoleColor(holeColor);
        mChart.setTransparentCircleColor(transColor);
        mChart.setTransparentCircleAlpha(110);
        mChart.setHoleRadius(holeRadius);
        mChart.setTransparentCircleRadius(transRadius);
    }

    private void setChartData() {
        PieDataSet dataSet = new PieDataSet(mEntries, "");
        dataSet.setSliceSpace(0f);
        dataSet.setSelectionShift(5f);
//        dataSet.setEntryLabelsColor(mValueColor);
        dataSet.setColors(mPieColors);
        //dataSet.setSelectionShift(0f);
        dataSet.setYValuePosition(mValuePosition);
        dataSet.setXValuePosition(mValuePosition);
        dataSet.setValueLineColor(mValueColor);
        dataSet.setSelectionShift(15f);
        dataSet.setValueLinePart1Length(0.6f);
        dataSet.setValueLineColor(mValueColor);
        PieData data = new PieData(dataSet);
        data.setValueFormatter(new PercentFormatter());
        data.setValueTextSize(mTextSize);
        data.setValueTextColor(mValueColor);
        data.setValueTextColor(mValueColor);
        mChart.setData(data);
        // undo all highlights
        mChart.highlightValues(null);
        mChart.invalidate();
    }

    /**
     * <p>说明文字是否可见</p>
     * @param enabled true 可见,默认可见
     */
    public void setLegendEnabled(boolean enabled) {
        mChart.getLegend().setEnabled(enabled);
        mChart.invalidate();
    }
    public void setPercentValues (boolean showPercent) {
        mChart.setUsePercentValues(showPercent);
    }
}

这样字可以直接运用了

/**
 * 添加数据均匀饼装图
 */
public void updatePieChart() {
    int[] colors = {Color.parseColor("#faa74c"), Color.parseColor("#58D4C5"), Color.parseColor("#36a3eb"), Color.parseColor("#cc435f"), Color.parseColor("#f1ea56"),
            Color.parseColor("#f49468"), Color.parseColor("#d5932c"), Color.parseColor("#34b5cc"), Color.parseColor("#8169c6"), Color.parseColor("#ca4561"),Color.parseColor("#fee335")};
    ArrayList<PieEntry> entries = new ArrayList<PieEntry>();
    for(int i = 0 ;i <= 5; i++){
        PieEntry pieEntry = new PieEntry(60,"项目" + i + "占比");
        entries.add(pieEntry);
    }

    for(int i = 6 ;i <= 7; i++){
        PieEntry pieEntry = new PieEntry(100,"项目" + i + "占比");
        entries.add(pieEntry);
    }

    PieEntry pieEntry = new PieEntry(100,"项目8占比");
    entries.add(pieEntry);

    if (entries.size() != 0) {
        PieChart new_pie_chart = (PieChart) mView.findViewById(R.id.new_pie_chart);
        PieChartEntity pieChartEntity = new PieChartEntity(new_pie_chart, entries, new String[]{"", "", ""}, colors, 12f, Color.GRAY, PieDataSet.ValuePosition.OUTSIDE_SLICE);
        pieChartEntity.setHoleEnabled(Color.TRANSPARENT, 40f, Color.TRANSPARENT, 40f);
        pieChartEntity.setLegendEnabled(false);
        pieChartEntity.setPercentValues(true);
    }
}

运行方法就能实现动态图中数据正常的饼状图
但当数据过小,并且连在一起是就有文字重合的问题



这个时候问题就来了。
解决方案,产品决定占比小于5%或者10%不显示,或另外注明显示。
这下初级程序员就GG了,找不到满足需求的控件Demo参考啊,自定义又写不出来,这时大神微微一笑:加个参数判断一下不就OK啦

思路:三方库的默认PieEntry(float value, String lable),我加个构造方法,加个参数PieEntry(float value, String lable, boolean display)用来判断传的value是否满足要求,然后通过boolean display,同时控制绘图部分,当display为false,我就不花向外线和文字。

先看改造后的PieEntry.Java(修改位置有注释)

public class PieEntry extends Entry {

    private String label;

    /**
     * 用来标记是否显示描述文字
     */
    private boolean display = true;

    public PieEntry(float value) {
        super(0f, value);
    }

    public PieEntry(float value, Object data) {
        super(0f, value, data);
    }

    public PieEntry(float value, String label) {
        super(0f, value);
        this.label = label;
    }

    public PieEntry(float value, String label, Object data) {
        super(0f, value, data);
        this.label = label;
    }

    /**
     * 当传数据过小,调用此方法不显示文字
     * @param value
     * @param label
     * @param display
     */
    public PieEntry(float value ,String label,boolean display){
        super(0f,value);
        this.label = label;
        this.display = display;
    }
    /**
     * This is the same as getY(). Returns the value of the PieEntry.
     *
     * @return
     */
    public float getValue() {
        return getY();
    }

    public String getLabel() {
        return label;
    }

    /**
     * 文字绘制时用到做判断
     * @return
     */
    public boolean isDisplay() {
        return display;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    /**
     * 设置display值,达到控制是否显示文字
     * @param display
     */
    public void setDisplay(boolean display) {
        this.display = display;
    }

    @Deprecated
    @Override
    public void setX(float x) {
        super.setX(x);
        Log.i("DEPRECATED", "Pie entries do not have x values");
    }

    @Deprecated
    @Override
    public float getX() {
        Log.i("DEPRECATED", "Pie entries do not have x values");
        return super.getX();
    }

    public PieEntry copy() {
        PieEntry e = new PieEntry(getY(), label, getData());
        return e;
    }
}

再看关于绘图的类PieChartRenderer.Java
这个类太长,主要修改方法是drawValues (修改位置有注释)

@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++) {

            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;

                    mValuePaint.setTextAlign(Align.RIGHT);

                    if(drawXOutside)
                        mEntryLabelsPaint.setTextAlign(Align.RIGHT);

                    labelPtx = pt2x - offset;
                    labelPty = pt2y;
                } else {
                    pt2x = pt1x + polyline2Width;
                    pt2y = pt1y;
                    mValuePaint.setTextAlign(Align.LEFT);

                    if(drawXOutside)
                        mEntryLabelsPaint.setTextAlign(Align.LEFT);

                    labelPtx = pt2x + offset;
                    labelPty = pt2y;
                }

                /**
                 * 这里是绘制圈外线所以须添加
                 */
                if (entry.isDisplay()) {
                    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&&entry.isDisplay()) {

                    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&&entry.isDisplay()) {
                        drawEntryLabel(c, entry.getLabel(), labelPtx, labelPty + lineHeight / 2.f);
                    }
                } else if (drawYOutside) {

                    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&&entry.isDisplay()) {

                    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();
}

总结:1.不敢不会修改开源源码的主要原因在于阅读源码能力不够
2.对于动画绘图部分基本看不懂,继续加强
3.像大神这样对开源代码做整理,封装好便于使用,这种封装优化思想需要学习
好了,就写到这里,如果给你带来帮助,记得关注一下博主哦!

写在最后微信扫码提问

image
上一篇下一篇

猜你喜欢

热点阅读