MPAndroidChart LineChart 折线图 你要的都在这里了

MPAndroidChart LineChart 折线图 你要的都在这里了_第1张图片

前言

  MPAndroidChart已经出了很长的一段时间,相信大家也有所耳闻,自己也使用了有一段时间,固在此写下文章,根据项目的需求,记录一些见解与问题,作为参考。望大家取其精华去其糟粕。


最终效果图

MPAndroidChart LineChart 折线图 你要的都在这里了_第2张图片

涉及到的问题以及知识点
  1. 图表样式以及基础数据 (快速入门)
  2. x轴标签自定义标签(Formatting Data Values (ValueFormatter))
  3. 自定义覆盖物(MarkerView)
  4. 自定义多个覆盖物(MarkerView)
  5. 默认选中覆盖物(Highlighting Values)
  6. 线条的隐藏以及显示(visible)
  7. 实现左右滑动
  8. 数据更新

当前演示 Demo


快速入门

1.编写布局文件

 <com.github.mikephil.charting.charts.LineChart
        android:id="@+id/chart"
        android:layout_width="match_parent"
        android:layout_height="195dp"
        />

2.实例化并且,设置x轴和y轴的点

mLineChart = findViewById(R.id.chart);

//1.设置x轴和y轴的点
List entries = new ArrayList<>();
   for (int i = 0; i < 12; i++)
      entries.add(new Entry(i, new Random().nextInt(300)));

3 .把数据赋值到你的线条

  LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset

4.设置数据刷新图表

//3.chart设置数据
  LineData lineData = new LineData(dataSet);
  mLineChart.setData(lineData);
  mLineChart.invalidate(); // refresh

MPAndroidChart LineChart 折线图 你要的都在这里了_第3张图片

很简单吧,但是离我们的效果图还差了好多现在我们开始完善样式,一步一步去设置

样式设置

1.线条样式

        LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset
        dataSet.setColor(Color.parseColor("#7d7d7d"));//线条颜色
        dataSet.setCircleColor(Color.parseColor("#7d7d7d"));//圆点颜色
        dataSet.setLineWidth(1f);//线条宽度

2.x和y轴样式

        //设置样式
        YAxis rightAxis = mLineChart.getAxisRight();

        //设置图表右边的y轴禁用
        rightAxis.setEnabled(false);
        YAxis leftAxis = mLineChart.getAxisLeft();
        //设置图表左边的y轴禁用
        leftAxis.setEnabled(false);
        //设置x轴
        XAxis xAxis = mLineChart.getXAxis();
        xAxis.setTextColor(Color.parseColor("#333333"));
        xAxis.setTextSize(11f);
        xAxis.setAxisMinimum(0f);
        xAxis.setDrawAxisLine(true);//是否绘制轴线
        xAxis.setDrawGridLines(false);//设置x轴上每个点对应的线
        xAxis.setDrawLabels(true);//绘制标签  指x轴上的对应数值
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//设置x轴的显示位置
        xAxis.setGranularity(1f);//禁止放大后x轴标签重绘

3.隐藏图例与描述


        //透明化图例
        Legend legend = mLineChart.getLegend();
        legend.setForm(Legend.LegendForm.NONE);
        legend.setTextColor(Color.WHITE);

        //隐藏x轴描述
        Description description = new Description();
        description.setEnabled(false);
        mLineChart.setDescription(description);

4.填充数据

        //chart设置数据
        LineData lineData = new LineData(dataSet);
        //是否绘制线条上的文字
        lineData.setDrawValues(false);
        mLineChart.setData(lineData);
        mLineChart.invalidate(); // refresh

效果图
MPAndroidChart LineChart 折线图 你要的都在这里了_第4张图片

是不是已经很接近效果图了,我们在格式化一下x轴标签

x轴标签自定义标签(Formatting Data Values (ValueFormatter))

格式化x轴标签有好几种方式,这里说两个方法
1.要么自己实现接口的方式

 XAxis xAxis = mLineChart.getXAxis();
 xAxis.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                return String.valueOf((int) value + 1).concat("月");
            }
        });

2.要么用库已经写好的类

//准备好每个点对应的x轴数值
List<String> list = new ArrayList<>();
 for (int i = 0; i < 12; i++) {
     list.add(String.valueOf(i+1).concat("月"));
 }
  XAxis xAxis = mLineChart.getXAxis();
 xAxis.setValueFormatter(new IndexAxisValueFormatter(list));

格式化Y轴也是同样的道理

效果图
MPAndroidChart LineChart 折线图 你要的都在这里了_第5张图片

自定义覆盖物(MarkerView)

(1) 继承MarkerView复写其中的方法就OJBK了

直接上代码解释吧 -v-

public class DetailsMarkerView extends MarkerView {

    private TextView mTvMonth;
    private TextView mTvChart1;

    /**
     * 在构造方法里面传入自己的布局以及实例化控件    
     * @param context 上下文
     * @param 自己的布局 
   */
    public DetailsMarkerView(Context context, int layoutResource) {
        super(context, layoutResource);
        mTvMonth = findViewById(R.id.tv_chart_month);
        mTvChart1 = findViewById(R.id.tv_chart_1);
    }

    //每次重绘,会调用此方法刷新数据
    @Override
    public void refreshContent(Entry e, Highlight highlight) {
        super.refreshContent(e, highlight);
        try {
            //收入
            if (e.getY() == 0) {
                mTvChart1.setText("暂无数据");
            } else {
                mTvChart1.setText(concat(e.getY(), "支出:"));
            }
            mTvMonth.setText(String.valueOf((int) e.getX() + 1).concat("月"));
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        super.refreshContent(e, highlight);
    }

    //布局的偏移量。就是布局显示在圆点的那个位置
    // -(width / 2) 布局水平居中
    //-(height) 布局显示在圆点上方
    @Override
    public MPPointF getOffset() {
        return new MPPointF(-(getWidth() / 2), -getHeight());
    }

    public String concat(float money, String values) {
        return values + new BigDecimal(money).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString() + "元";
    }

}

(2) 设置覆盖物

DetailsMarkerView detailsMarkerView = new DetailsMarkerView(this);
//一定要设置这个玩意,不然到点击到最边缘的时候不会自动调整布局
detailsMarkerView.setChartView(mLineChart);
mLineChart.setDetailsMarkerView(detailsMarkerView);

效果图

MPAndroidChart LineChart 折线图 你要的都在这里了_第6张图片


自定义多个覆盖物(MarkerView)

接下来我们继续完善,达到下面的效果图

MPAndroidChart LineChart 折线图 你要的都在这里了_第7张图片

要达到上面的效果,我们可以把它当作3个覆盖物
就是这个意思
MPAndroidChart LineChart 折线图 你要的都在这里了_第8张图片

1.先定义好3个覆盖物,DetailsMarkerView(详情),PositionMarker (中间的标杆)RoundMarker(圆点)

class DetailsMarkerView  extends MarkerView{...}
class PositionMarker  extends MarkerView{...}
class RoundMarkerextends MarkerView{...}

2.继承LineChart,重写drawMarkers 方法。我们直接把drawMarkers方法直接复制下来,加上自己所需要的MarkerView,然后计算它们的位置即可

public class MyLineChart extends LineChart {
      //弱引用覆盖物对象,防止内存泄漏,不被回收
    private WeakReference mDetailsReference;
    private WeakReference mRoundMarkerReference;
    private WeakReference mPositionMarkerReference;

    /**
     * 所有覆盖物是否为空
     *
     * @return TRUE FALSE
     */
    public boolean isMarkerAllNull() {
        return mDetailsReference.get() == null && mRoundMarkerReference.get() == null && mPositionMarkerReference.get() == null;
    }

    public void setDetailsMarkerView(DetailsMarkerView detailsMarkerView) {
        mDetailsReference = new WeakReference<>(detailsMarkerView);
    }

    public void setRoundMarker(RoundMarker roundMarker) {
        mRoundMarkerReference = new WeakReference<>(roundMarker);
    }

    public void setPositionMarker(PositionMarker positionMarker) {
        mPositionMarkerReference = new WeakReference<>(positionMarker);
    }


   /**
      复制父类的 drawMarkers方法,并且更换上自己的markerView
     * draws all MarkerViews on the highlighted positions
     */
    protected void drawMarkers(Canvas canvas) {
        DetailsMarkerView mDetailsMarkerView = mDetailsReference.get();
        RoundMarker mRoundMarker = mRoundMarkerReference.get();
        PositionMarker mPositionMarker = mPositionMarkerReference.get();

        // if there is no marker view or drawing marker is disabled
        if (mDetailsMarkerView == null || mRoundMarker == null || mPositionMarker == null || !isDrawMarkersEnabled() || !valuesToHighlight())
            return;

        for (int i = 0; i < mIndicesToHighlight.length; i++) {

            Highlight highlight = mIndicesToHighlight[i];

            IDataSet set = mData.getDataSetByIndex(highlight.getDataSetIndex());

            Entry e = mData.getEntryForHighlight(mIndicesToHighlight[i]);

            int entryIndex = set.getEntryIndex(e);

            // make sure entry not null
            if (e == null || entryIndex > set.getEntryCount() * mAnimator.getPhaseX())
                continue;

            float[] pos = getMarkerPosition(highlight);

            LineDataSet dataSetByIndex = (LineDataSet) getLineData().getDataSetByIndex(highlight.getDataSetIndex());

            // check bounds
            if (!mViewPortHandler.isInBounds(pos[0], pos[1]))
                continue;

            float circleRadius = dataSetByIndex.getCircleRadius();

            //pos[0], pos[1] x 和 y 
            // callbacks to update the content
            mDetailsMarkerView.refreshContent(e, highlight);

            mDetailsMarkerView.draw(canvas, pos[0], pos[1] - mPositionMarker.getHeight());


            mPositionMarker.refreshContent(e, highlight);
            mPositionMarker.draw(canvas, pos[0] - mPositionMarker.getWidth() / 2, pos[1] - mPositionMarkerl.getHeight());

            mRoundMarker.refreshContent(e, highlight);
            mRoundMarker.draw(canvas, pos[0] - mRoundMarker.getWidth() / 2, pos[1] + circleRadius - mRoundMarker.getHeight());
        }
}

设置覆盖物 activity主要代码

protected void onCreate(Bundle savedInstanceState) {
    ......
    //点击图表坐标监听
        mLineChart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
            @Override
            public void onValueSelected(Entry e, Highlight h) {
                //查看覆盖物是否被回收
                if (mLineChart.isMarkerAllNull()) {
                    //重新绑定覆盖物
                    createMakerView();
                    //并且手动高亮覆盖物
                    mLineChart.highlightValue(h);
                }
            }

            @Override
            public void onNothingSelected() {

            }
        });
    ......
}


  /**
    * 创建覆盖物
    */
   public void createMakerView() {
       DetailsMarkerView detailsMarkerView = new DetailsMarkerView(this);
       detailsMarkerView.setChartView(mLineChart);
       mLineChart.setDetailsMarkerView(detailsMarkerView);
       mLineChart.setPositionMarker(new PositionMarker(this));
       mLineChart.setRoundMarker(new RoundMarker(this));
   }

这样就大功告成啦!!


默认显示覆盖物(Highlighting Values)

MPAndroidChart LineChart 折线图 你要的都在这里了_第9张图片

可以通过上面的方法,默认显示覆盖物,比如

   //默认显示第一个覆盖物
   mLineChart.highlightValue(0,0);

线条的隐藏以及显示(Highlighting Values)

可以通过LineChart对象获取到线条LineDataSet实体类。然后调用LineDataSet.setVisible(true或者false);,进行隐藏或显示

mLineChart.getLineData().getDataSets().get(0).setVisible(true);

左右滑动,并动态切换放大倍数

MPAndroidChart LineChart 折线图 你要的都在这里了_第10张图片

代码

//x放大5倍  1f代表不放大
mLineChart.zoomToCenter(5, 1f);


//切记如果要动态的更换倍数,或者还原倍数一定要调用下面的这个方法停止惯性滑动
//不然在拖动过程当中是无法更换倍数
BarLineChartTouchListener barLineChartTouchListener = (BarLineChartTouchListener) mLineChart.getOnTouchListener();
 barLineChartTouchListener.stopDeceleration();

更新数据

主要的逻辑:
1. 准备要更新的数据源
2. 检查是否有LineDataSet 存在
3. 有,则通过LineDataSet 的setValues更换整个坐标,或者 data.addEntry(…) 添加一个或者 data.removeEntry(…)删除一个。
4. 无,则创建LineDataSet ,重新构造数据源
4. 调用mLineChart.invalidate();更新图表

代码实例:

 //1,准备要更换的数据
     List<Entry> entries = new ArrayList<>();
        for (int i = 0; i < 12; i++)
            entries.add(new Entry(i, new Random().nextInt(300)));

        //2. 获取LineDataSet线条数据集
        List<ILineDataSet> dataSets = mLineChart.getLineData().getDataSets();

       //是否存在
         if (dataSets != null && dataSets.size() > 0) {
             //直接更换数据源
             for (ILineDataSet set : dataSets) {
                 LineDataSet data = (LineDataSet) set;
                 data.setValues(entries);
             }
         } else {
             //重新生成LineDataSet线条数据集
             LineDataSet dataSet = new LineDataSet(entries, "Label"); // add entries to dataset
            dataSet.setDrawCircles(false);
               dataSet.setColor(Color.parseColor("#7d7d7d"));//线条颜色
               dataSet.setCircleColor(Color.parseColor("#7d7d7d"));//圆点颜色
               dataSet.setLineWidth(1f);//线条宽度
               LineData lineData = new LineData(dataSet);
               //是否绘制线条上的文字
               lineData.setDrawValues(false);
               mLineChart.setData(lineData);
           }
           //更新
           mLineChart.invalidate();

折线图的内容暂时就那么多,如果有不懂的可以留言,希望可以帮到大家。

最后附上 Demo

你可能感兴趣的:(图表)