MPAndroidChart添加自定義樣式

之前爲添加XY軸描述大傷腦筋,因爲如果用原生的TextView在圖表控件的上方和下方分別作爲XY軸加了Legend之後效果會差很多,如果繪製在圖表上方又不好控制位置。
之前用textView來左XY軸實現效果:
實現效果
實現效果總的來講還是可以的,但是因爲控件是分離的,不好做到適配。
如果能在圖表裏面繪製一個就好了,然後發現了圖表有Legend的繪製,有得參考了,自己根據Legend寫了一個。
話不多說,開搞

分析Legend實現

Android Studio雙擊Shift找Legend,在框架的components文件夾下找到了Legend這個類。
Legend裏面配置了一堆Legend參數:位置,顏色,方向,顯示的數據等等…嗯,知道了這個就是用來配置Legend屬性的類。

components裏面的屬性類都繼承了ComponentBase這個類,看一下ComponentBase這個類做什麼的。
裏面是一些抽取出來的共用的配置,文字大小,顏色,是否可用間距等…

Legend在框架中的引用:LegendEntry,LegendRender,BarLineChartBase,Chart,BaseDataSet,IDataSet都有做引用

  1. LegendEntry:跟BarEntry等是一樣的,是Legend每個item的屬性。因爲Legend 有多個數據,所以需要一個Entry的列表的來做存儲。
  2. LegendRender:這個是重點,它用來根據屬性配置繪製Legend。
  3. BarLineChartBase:這個在這個類的onDraw方法找到了Legend和LegendRender的引用。繪製就在這裏了。
  4. Chart:聲明Legend和LegendRender,這個是公用的屬性。
  5. BaseDataSet和IDataSet:因爲Legend跟圖表的顏色和label有掛鉤,所以需要。

分析上面文件我們需要寫兩個類,XYDESC(屬性配置類),XYDESCRender(繪製類),並且在BarLineChartBase中聲明和繪製(只有這兩個圖有XY軸)

實現我們的XYDESC

在components文件夾下增加XYDESC類


import android.graphics.Paint;

import com.github.mikephil.charting.utils.Utils;

/**
 * @author xiaolong
 * @version v1.0
 * @function <描述功能>
 * @date 2016/10/12-14:09
 */
public class XYDesc extends ComponentBase {
    private String xDesc;
    private String yDesc;
    private float yPadding;
    public String getxDesc() {
        return xDesc;
    }

    public void setxDesc(String xDesc) {
        this.xDesc = xDesc;
    }

    public String getyDesc() {
        return yDesc;
    }

    public void setyDesc(String yDesc) {
        this.yDesc = yDesc;
    }

    public XYDesc() {
        this.setEnabled(false);
    }


    /**
     * returns the maximum length in pixels across all legend labels + formsize
     * + formtotextspace
     *
     * @param p the paint object used for rendering the text
     * @return
     */
    public float getMaximumEntryWidth(Paint p, String desc) {
        float length = (float) Utils.calcTextWidth(p, desc);
        return length;
    }

    /**
     * returns the maximum height in pixels across all legend labels
     *
     * @param p the paint object used for rendering the text
     * @return
     */
    public float getMaximumEntryHeight(Paint p, String desc) {
        float length = (float) Utils.calcTextHeight(p, desc);
        return length;
    }

    public float getyPadding() {
        return yPadding;
    }

    public void setyPadding(float yPadding) {
        this.yPadding = yPadding;
    }
}

分別設置了X軸和Y軸描述,
padding是因爲我們是參照Legend寫的如果不做padding的話,會跟Legend繪製重疊,所以如果在左上角或右下角有Legend的時候加上padding。

//這兩個方法用來計算文字的寬度和高度(Render中需要)

getMaxinumEntryWidth(Paint p,String desc)

getMaximumEntryHeight(Paint p, String desc)

在renderer文件夾下增加XYDESCRender

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;

import com.github.mikephil.charting.components.XYDesc;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;

/**
 * @author xiaolong
 * @version v1.0
 * @function <描述功能>
 * @date 2016/10/12-14:05
 */
public class XYDESCRenderer extends Renderer {


    /**
     * paint for the XYDESCRenderer labels
     */
    protected Paint mXYDESCLabelPaint;
    protected XYDesc mXYDesc;

    public XYDESCRenderer(ViewPortHandler viewPortHandler, XYDesc xydesc) {
        super(viewPortHandler);
        mXYDesc = xydesc;
        //初始化畫筆
        mXYDESCLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mXYDESCLabelPaint.setTextSize(Utils.convertDpToPixel(xydesc.getTextSize()));
        mXYDESCLabelPaint.setTextAlign(Paint.Align.LEFT);
    }

    /**
     * Returns the Paint object used for drawing the Legend labels.
     *
     * @return
     */
    public Paint getLabelPaint() {
        return mXYDESCLabelPaint;
    }

    /**
     * 在畫布上繪製
     * @param c
     */
    public void renderLegend(Canvas c) {
        if (!mXYDesc.isEnabled())
            return;
        Typeface tf = mXYDESCLabelPaint.getTypeface();

        if (tf != null)
            mXYDESCLabelPaint.setTypeface(tf);

        mXYDESCLabelPaint.setTextSize(mXYDesc.getTextSize());
        mXYDESCLabelPaint.setColor(mXYDesc.getTextColor());
        //獲取行高
        float labelLineHeight = Utils.getLineHeight(mXYDESCLabelPaint);
        float yoffset = mXYDesc.getYOffset();
        float xoffset = mXYDesc.getXOffset();
//        drawLabel(c, mViewPortHandler.contentLeft() + mXYDesc.getMaximumEntryWidth(mXYDESCLabelPaint, mXYDesc.getyDesc()), yoffset + labelLineHeight + mXYDesc.getyPadding(), mXYDesc.getyDesc());
        drawLabel(c, mViewPortHandler.contentLeft()
                , yoffset + labelLineHeight + mXYDesc.getyPadding(), mXYDesc.getyDesc());
        drawLabel(c, mViewPortHandler.contentRight() - mXYDesc.getMaximumEntryWidth(mXYDESCLabelPaint, mXYDesc.getxDesc())
                , mViewPortHandler.getChartHeight() - yoffset - mXYDesc.getMaximumEntryHeight(mXYDESCLabelPaint, mXYDesc.getxDesc()) + 10, mXYDesc.getxDesc());

    }

    /**
     * Draws the provided label at the given position.
     *
     * @param c     canvas to draw with
     * @param x
     * @param y
     * @param label the label to draw
     */
    protected void drawLabel(Canvas c, float x, float y, String label) {
        c.drawText(label, x, y, mXYDESCLabelPaint);
    }


}

固定兩個drawLabel分別在左上角和右下角。

然後在BarLineChartBase聲明XYDESC和XYDESCRender

protected XYDesc mXYDesc;
protected XYDESCRenderer mXYDESCRenderer;
//在init方法中初始化
mXYDesc = new XYDesc();
mXYDESCRenderer = new XYDESCRenderer(mViewPortHandler, mXYDesc);
//在onDraw方法中繪製
找到 mLegendRenderer.renderLegend(canvas);下面加一句
mXYDESCRenderer.renderLegend(canvas);
增加屬性設置方法
  public XYDesc getXYDesc() {
        return mXYDesc;
    }

    public XYDESCRenderer getXYDESCRenderer() {
        return mXYDESCRenderer;
    }


    public void setXYDesc(String xDesc, String yDesc) {
        setXYDesc(xDesc, yDesc, 10f);
    }

    public void setXYDesc(String xDesc, String yDesc, float textSize) {
        setXYDesc(xDesc, yDesc, textSize, Color.BLACK);
    }

    public void setXYDesc(String xDesc, String yDesc, float textSize, int textColor) {
        mXYDesc.setxDesc(xDesc);
        mXYDesc.setyDesc(yDesc);
        mXYDesc.setTextSize(textSize);
        mXYDesc.setTextColor(textColor);
        mXYDesc.setEnabled(true);

        if (mLegend.isEnabled() && (mLegend.getPosition() == Legend.LegendPosition.ABOVE_CHART_CENTER
                || mLegend.getPosition() == Legend.LegendPosition.ABOVE_CHART_LEFT
                || mLegend.getPosition() == Legend.LegendPosition.ABOVE_CHART_RIGHT)) {
            mXYDesc.setyPadding(6 * mLegend.getFormSize());
        }
        this.setExtraBottomOffset(12f + textSize);
        this.setExtraTopOffset(12f + textSize);
    }

就可以參考第一篇的來使用了。

package cn.xiaolongonly.mpchartsample.ui;

import android.graphics.Color;

import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;

import java.util.ArrayList;
import java.util.List;

import cn.xiaolongonly.mpchartsample.R;
import cn.xiaolongonly.mpchartsample.base.BaseActivity;
import cn.xiaolongonly.mpchartsample.chart.markview.DataMarkView;
import cn.xiaolongonly.mpchartsample.chart.util.ColorTemplate;

/**
 * @author xiaolong
 * @version v1.0
 * @function <描述功能>
 * @date 2016/12/6-9:10
 */
public class LineChartActivity1 extends BaseActivity {
    private LineChart chart;

    @Override
    protected int getLayoutId() {
        return R.layout.list_item_linechart;
    }

    @Override
    protected void initView() {
        chart = findView(R.id.chart);

        ChartConfig();
        //XY軸配置
        XAxis xAxis = chart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); //定製X軸是在圖表上方還是下方。
        xAxis.setDrawGridLines(false);
        xAxis.setGranularity(1);
        xAxis.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                return (int) value + "年";
            }
        });
        YAxis yAxisRight = chart.getAxisRight();
        yAxisRight.setEnabled(false);
        YAxis yAxisLeft = chart.getAxisLeft();
        yAxisLeft.setAxisMinValue(0);
        //動畫效果
        chart.animateX(750);
        chart.animateY(750);
    }

    /**
     * 圖表的配置 一些提示和Legend
     */
    private void ChartConfig() {

        //設置覆蓋物
        DataMarkView dataMarkView = new DataMarkView(this, 0, "");//自定義覆蓋物
        chart.setMarkerView(dataMarkView);

        //背景設置
        chart.setDrawGridBackground(false);//表格背景繪製
        chart.setBackgroundColor(getResources().getColor(R.color.chart_bg));

        //Legend定製
        chart.getLegend().setPosition(Legend.LegendPosition.ABOVE_CHART_LEFT);
        chart.getLegend().setForm(Legend.LegendForm.CIRCLE);//Legend樣式

        //圖表描述
        chart.setDescription(null);
        // 設置無數據文本提示
        chart.setNoDataText(getResources().getString(R.string.chart_no_data));
        //XY軸描述
        chart.setXYDesc("年份", "總金額(元)");
        //設置單方向和雙方向縮放 true x,y方向可以同時控制,false只能控制x方向的縮小放大或者Y方向的縮小放大
        chart.setPinchZoom(true);
        //填充數據
        chart.setData(new LineData(generateLineDataSet()));
    }

    @Override
    protected void setListener() {

    }


    private ILineDataSet generateLineDataSet() {
        int color = ColorTemplate.PIE_COLORS[0];
        LineDataSet dataSet = new LineDataSet(generateData(), "年度營業額曲線");
        dataSet.setLineWidth(2.0f);
        dataSet.setCircleRadius(3.5f);
        dataSet.setDrawCircleHole(true);//填充圓
        dataSet.setValueTextSize(9f);
        dataSet.setHighlightLineWidth(2.0f);
        dataSet.setDrawFilled(true);//區域顏色
        dataSet.setFillAlpha(51);
        dataSet.setFillColor(color); //填充色
        dataSet.setHighLightColor(color); //選中十字線色
        dataSet.setColor(color); //線條顏色
        dataSet.setCircleColor(color); //圓點顏色
        dataSet.setCircleColorHole(Color.WHITE);
        dataSet.setCircleHoleRadius(2.0f);
        dataSet.setDrawValues(false);
        return dataSet;
    }

    private List<Entry> generateData() {
        List<Entry> entryList = new ArrayList<>();
        entryList.add(new Entry(2013, 1000));
        entryList.add(new Entry(2014, 2000));
        entryList.add(new Entry(2015, 3000));
        entryList.add(new Entry(2016, 4000));
        return entryList;
    }
}

效果如下
效果圖

將描述寫到了圖表框架中了。感覺明顯就好用多了。

在很多需求中,大部分圖表都是大同小異。我們並不想要將同種類型的圖表都按需求配置一遍,那樣代碼會顯得多且雜,所以有必要將這些配置寫成通用的配置。

下一篇會介紹圖表框架的簡單封裝。

框架地址:https://github.com/PhilJay/MPAndroidChart

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章