MPAndroidChart常用的图标库样例

0x01 前言

MPAndroidChart的图标库非常强大,参考他自带的demo就可以实现我们的需求,这里介绍一些demo里没有或网上资料比较少的样例。(主要介绍饼状图和水平条形图)
先上样式图,github地址下载:MPChartDemo

yanshi.gif

0x02. 饼状图

有时候我们饼状图的需求要求我们画出的标签折线颜色和饼状图的颜色一致,但MPAndroidChart并没有提供我们更改标签折线颜色的接口,如何解决这样的问题?


device-2018-06-29-154936.png

我们可以重新封装一下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();                    //将图表重绘以显示设置的属性和数据
//        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容