MPAndroidChart(3)曲线图LineChart(多条

2022-10-27  本文已影响0人  古早味蛋糕

本文相关代码

MPAndroidChart在github地址:https://github.com/PhilJay/MPAndroidChart

一.效果图

曲线动态添加数据(X轴为当前时间)

1.png

二.实现效果

1.X轴为当前时间,只需要Y轴数据即可

2.X轴的值为字符串,而Entry的构造方法参数全为float public Entry(float x, float y) 所以需要另外定义X轴的值

三.简要核心代码

动态创建一条曲线

  /**
 * 功能:动态创建一条曲线
 */
private void createLine(LineDataSet lineDataSet, int color, LineData lineData, LineChart lineChart) {

    // 初始化线条
    initLineDataSet(lineDataSet, color, LineDataSet.Mode.CUBIC_BEZIER);

    if (lineData == null) {
        lineData = new LineData();
        lineData.addDataSet(lineDataSet);
        lineChart.setData(lineData);
    } else {
        lineChart.getLineData().addDataSet(lineDataSet);
    }

    lineChart.invalidate();
}

动态添加Entry数据

/**
 * 动态添加数据
 * 在一个LineChart中存放的折线,其实是以索引从0开始编号的
 *
 * @param yValues y值
 */
public void addEntry(LineData lineData, LineChart lineChart, float yValues, int index) {

    // 通过索引得到一条曲线,之后得到折线上当前点的数量
    int xCount = lineData.getDataSetByIndex(index).getEntryCount();

    SimpleDateFormat df = new SimpleDateFormat("mm:ss");

    Entry entry = new Entry(lineData.getEntryCount(), yValues); // 创建一个点
    lineData.addEntry(entry, index); // 将entry添加到指定索引处的折线中
    Log.d("LineChartDynamicActivity", "addEntry: "+entry.getX());
    //通知数据已经改变
    lineData.notifyDataChanged();
    lineChart.notifyDataSetChanged();

    //把yValues移到指定索引的位置
    lineChart.moveViewToAnimated(xCount - 4, yValues, YAxis.AxisDependency.LEFT, 1000);
    lineChart.invalidate();
}

使用Handle机制进行循环添加数据,以达到不停的动态显示曲线的效果。
核心代码

/**
 * 功能:发送开始
 */
public void sendStartAddEntry() {
    if (!mDynamicHandler.hasMessages(MSG_START)) { // 判断是否有消息队列此消息,如果没有则发送
        mDynamicHandler.sendEmptyMessageDelayed(MSG_START, 1000);
    }
}

停止动态显示代码

     /**
 * 功能:暂停添加点,即移除所有消息
 */
public void sendPauseAddEntry() {
    mDynamicHandler.removeCallbacksAndMessages(null);
}

Handle的完整实现

/**
 * 功能:自定义Handler,通过弱引用的方式防止内存泄漏
*/
public class DynamicHandler extends Handler {
public static final int MSG_START = 1; // handler消息,开始添加点
WeakReference<LineChartDynamicActivity> mReference;

public DynamicHandler(LineChartDynamicActivity activity) {
    mReference = new WeakReference<>(activity);
}


@Override
public void handleMessage(Message msg) {
    super.handleMessage(msg);
    LineChartDynamicActivity lineChartDynamicActivity = mReference.get();
    if (lineChartDynamicActivity == null) {
        return;
    }
    switch (msg.what) {
        case MSG_START:
            lineChartDynamicActivity.addLine1Data(lineChartDynamicActivity.getRandom(30f));
            lineChartDynamicActivity.addLine2Data(lineChartDynamicActivity.getRandom(20f));
            lineChartDynamicActivity.addLine3Data(lineChartDynamicActivity.getRandom(10f));
            lineChartDynamicActivity.sendStartAddEntry();
            break;
        default:
    }
 }
}

四.完整的代码实现
布局XML实现

 <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LineChartDynamicActivity">

 <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

    <CheckBox
        android:id="@+id/demo_checkbox1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:checked="true"
        android:text="曲线1" />

    <CheckBox
        android:id="@+id/demo_checkbox2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="曲线2" />

    <CheckBox
        android:id="@+id/demo_checkbox3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="曲线3" />

    <Button
        android:id="@+id/demo_start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始"/>

    <Button
        android:id="@+id/demo_pause"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="暂停"/>

 </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

完整的Activity实现,

public class LineChartDynamicActivity extends AppCompatActivity implements View.OnClickListener {
public static final int MSG_START = 1; // handler消息,开始添加点

// 曲线编号
public static final int LINE_NUMBER_1 = 0;
public static final int LINE_NUMBER_2 = 1;
public static final int LINE_NUMBER_3 = 2;

/**
 * 功能:启动方式
 */
public void startActivity(Context context) {
    context.startActivity(new Intent(context, LineChartDynamicActivity.class));
}

private DynamicHandler mDynamicHandler; // 自定义Handler
private Random mRandom = new Random(); // 随机产生点
private DecimalFormat mDecimalFormat = new DecimalFormat("#.00");   // 格式化浮点数位两位小数
private SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");//设置日期格式
private Map<Integer,String> timeDate = new HashMap<Integer,String>();//存储x轴的时间

Button mBtnStart;   // 开始添加点
Button mBtnPause;   // 暂停添加点
CheckBox mCheckBox1;
CheckBox mCheckBox2;
CheckBox mCheckBox3;
List<CheckBox> mCheckBoxList = new ArrayList<>();

LineChart mLineChart; // 曲线表,存线集合
LineData mLineData; // 线集合,所有折现以数组的形式存到此集合中
XAxis mXAxis; //X轴
YAxis mLeftYAxis; //左侧Y轴
YAxis mRightYAxis; //右侧Y轴
Legend mLegend; //图例
LimitLine mLimitline; //限制线

//  Y值数据链表
List<Float> mList1 = new ArrayList<>();
List<Float> mList2 = new ArrayList<>();
List<Float> mList3 = new ArrayList<>();

// Chart需要的点数据链表
List<Entry> mEntries1 = new ArrayList<>();
List<Entry> mEntries2 = new ArrayList<>();
List<Entry> mEntries3 = new ArrayList<>();

// LineDataSet:点集合,即一条线
LineDataSet mLineDataSet1 = new LineDataSet(mEntries1, "曲线1");
LineDataSet mLineDataSet2 = new LineDataSet(mEntries2, "曲线2");
LineDataSet mLineDataSet3 = new LineDataSet(mEntries3, "曲线3");

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_line_chart_dynamic);
    initView();
    initLineChart();
    mDynamicHandler = new DynamicHandler(this);

    //线条的渐变背景
    Drawable drawable1 = ContextCompat.getDrawable(this, R.drawable.fade_red);
    setChartFillDrawable(drawable1, mLineDataSet1);
    Drawable drawable2 = ContextCompat.getDrawable(this, R.drawable.fade_yellow);
    setChartFillDrawable(drawable2, mLineDataSet2);
    Drawable drawable3 = ContextCompat.getDrawable(this, R.drawable.fade_blue);
    setChartFillDrawable(drawable3, mLineDataSet3);

}


/**
 * 功能:产生随机数(小数点两位)
 */
public Float getRandom(Float seed) {
    return Float.valueOf(mDecimalFormat.format(mRandom.nextFloat() * seed));
}

/**
 * 功能:初始化基本控件,button,checkbox
 */
public void initView() {
    mBtnStart = findViewById(R.id.demo_start);
    mBtnPause = findViewById(R.id.demo_pause);
    mCheckBox1 = findViewById(R.id.demo_checkbox1);
    mCheckBox2 = findViewById(R.id.demo_checkbox2);
    mCheckBox3 = findViewById(R.id.demo_checkbox3);
    mCheckBoxList.add(mCheckBox1);
    mCheckBoxList.add(mCheckBox2);
    mCheckBoxList.add(mCheckBox3);

    mBtnStart.setOnClickListener(this);
    mBtnPause.setOnClickListener(this);
    mCheckBox1.setOnClickListener(this);
    mCheckBox2.setOnClickListener(this);
    mCheckBox3.setOnClickListener(this);

}

/**
 * 功能:初始化LineChart
 */
public void initLineChart() {
    mLineChart = findViewById(R.id.demo_linechart);
    mXAxis = mLineChart.getXAxis(); // 得到x轴
    mLeftYAxis = mLineChart.getAxisLeft(); // 得到侧Y轴
    mRightYAxis = mLineChart.getAxisRight(); // 得到右侧Y轴
    mLegend = mLineChart.getLegend(); // 得到图例
    mLineData = new LineData();
    mLineChart.setData(mLineData);

    // 设置图标基本属性
    setChartBasicAttr(mLineChart);

    //初始化添加第一个时间数据
    timeDate.put(0,df.format(System.currentTimeMillis()));
    // 设置XY轴
    setXYAxis(mLineChart, mXAxis, mLeftYAxis, mRightYAxis);
    mXAxis.setValueFormatter(new ValueFormatter() {
        @Override
        public String getAxisLabel(float value, AxisBase axis) {
            return timeDate.get((int) value % timeDate.size());
        }
    });
    // 添加线条
    initLine();

    // 设置图例
    createLegend(mLegend);

    // 设置MarkerView
    setMarkerView(mLineChart);
}


/**
 * 功能:设置图标的基本属性
 */
public void setChartBasicAttr(LineChart lineChart) {
    /***图表设置***/
    lineChart.setDrawGridBackground(false); //是否展示网格线
    lineChart.setDrawBorders(true); //是否显示边界
    lineChart.setDragEnabled(true); //是否可以拖动
    lineChart.setScaleEnabled(true); // 是否可以缩放
    lineChart.setTouchEnabled(true); //是否有触摸事件
    //设置XY轴动画效果
    //lineChart.animateY(2500);
    lineChart.animateX(1500);
}

/**
 * 功能:设置XY轴
 */
void setXYAxis(LineChart lineChart, XAxis xAxis, YAxis leftYAxis, YAxis rightYAxis) {
    /***XY轴的设置***/
    xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); //X轴设置显示位置在底部
    xAxis.setAxisMinimum(0f); // 设置X轴的最小值
    xAxis.setAxisMaximum(30); // 设置X轴的最大值
    xAxis.setLabelCount(20, false); // 设置X轴的刻度数量,第二个参数表示是否平均分配
    xAxis.setGranularity(1f); // 设置X轴坐标之间的最小间隔

    lineChart.setVisibleXRangeMaximum(5);// 当前统计图表中最多在x轴坐标线上显示的总量
    //保证Y轴从0开始,不然会上移一点
    leftYAxis.setAxisMinimum(0f);
    rightYAxis.setAxisMinimum(0f);
    leftYAxis.setAxisMaximum(100f);
    rightYAxis.setAxisMaximum(100f);
    leftYAxis.setGranularity(1f);
    rightYAxis.setGranularity(1f);
    leftYAxis.setLabelCount(20);
    lineChart.setVisibleYRangeMaximum(30, YAxis.AxisDependency.LEFT);// 当前统计图表中最多在Y轴坐标线上显示的总量
    lineChart.setVisibleYRangeMaximum(30, YAxis.AxisDependency.RIGHT);// 当前统计图表中最多在Y轴坐标线上显示的总量
    leftYAxis.setEnabled(false);

  //leftYAxis.setCenterAxisLabels(true);// 将轴标记居中
  // leftYAxis.setDrawZeroLine(true); // 原点处绘制 一条线
  //leftYAxis.setZeroLineColor(Color.RED);
  //leftYAxis.setZeroLineWidth(1f);
}

/**
 * 功能:对图表中的曲线初始化,添加三条,并且默认显示第一条
 */
private void initLine() {

    createLine(mLineDataSet1, Color.RED, mLineData, mLineChart);
    createLine(mLineDataSet2, Color.YELLOW, mLineData, mLineChart);
    createLine(mLineDataSet3, Color.BLUE, mLineData, mLineChart);


    // mLineData.getDataSetCount() 总线条数
    // mLineData.getEntryCount() 总点数
    // mLineData.getDataSetByIndex(index).getEntryCount() 索引index处曲线的总点数
    // 每条曲线添加到mLineData后,从索引0处开始排列
    for (int i = 0; i < mLineData.getDataSetCount(); i++) {
        mLineChart.getLineData().getDataSets().get(i).setVisible(false); //
    }
    showLine(LINE_NUMBER_1);
}

/**
 * 功能:根据索引显示或隐藏指定线条
 */
public void showLine(int index) {
    mLineChart
            .getLineData()
            .getDataSets()
            .get(index)
            .setVisible(mCheckBoxList.get(index).isChecked());
    mLineChart.invalidate();
}

/**
 * 功能:动态创建一条曲线
 */
private void createLine(LineDataSet lineDataSet, int color, LineData lineData, LineChart lineChart) {

    // 初始化线条
    initLineDataSet(lineDataSet, color, LineDataSet.Mode.CUBIC_BEZIER);

    if (lineData == null) {
        lineData = new LineData();
        lineData.addDataSet(lineDataSet);
        lineChart.setData(lineData);
    } else {
        lineChart.getLineData().addDataSet(lineDataSet);
    }

    lineChart.invalidate();
}


/**
 * 曲线初始化设置,一个LineDataSet 代表一条曲线
 *
 * @param lineDataSet 线条
 * @param color       线条颜色
 * @param mode
 */
private void initLineDataSet(LineDataSet lineDataSet, int color, LineDataSet.Mode mode) {
    lineDataSet.setColor(color); // 设置曲线颜色
    lineDataSet.setCircleColor(color);  // 设置数据点圆形的颜色
    lineDataSet.setDrawCircleHole(false);// 设置曲线值的圆点是否是空心
    lineDataSet.setLineWidth(1f); // 设置曲线宽度
    lineDataSet.setCircleRadius(3f); // 设置折现点圆点半径
    lineDataSet.setValueTextSize(10f);

    lineDataSet.setDrawFilled(true); //设置曲线图填充
    lineDataSet.setFormLineWidth(1f);
    lineDataSet.setFormSize(15.f);
    if (mode == null) {
        //设置曲线展示为圆滑曲线(如果不设置则默认曲线)
        lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
    } else {
        lineDataSet.setMode(mode);
    }

}


/**
 * 功能:创建图例
 */
private void createLegend(Legend legend) {
    /***曲线图例 标签 设置***/
    //设置显示类型,LINE CIRCLE SQUARE EMPTY 等等 多种方式,查看LegendForm 即可
    legend.setForm(Legend.LegendForm.CIRCLE);
    legend.setTextSize(12f);
    //显示位置 左下方
    legend.setVerticalAlignment(Legend.LegendVerticalAlignment.BOTTOM);
    legend.setHorizontalAlignment(Legend.LegendHorizontalAlignment.LEFT);
    legend.setOrientation(Legend.LegendOrientation.HORIZONTAL);
    //是否绘制在图表里面
    legend.setDrawInside(false);
    legend.setEnabled(true);
}


/**
 * 设置线条填充背景颜色
 *
 * @param drawable
 */
public void setChartFillDrawable(Drawable drawable, LineDataSet lineDataSet) {
    //避免在 initLineDataSet()方法中 设置了 lineDataSet.setDrawFilled(false); 而无法实现效果
    lineDataSet.setDrawFilled(true);
    lineDataSet.setFillDrawable(drawable);
    mLineChart.invalidate();
}


/**
 * 设置 可以显示X Y 轴自定义值的 MarkerView
 */
public void setMarkerView(LineChart lineChart) {
    DynamicLineChartMarkView mv = new DynamicLineChartMarkView(this);
    mv.setChartView(lineChart);
    lineChart.setMarker(mv);
    lineChart.invalidate();
}


/**
 * 动态添加数据
 * 在一个LineChart中存放的曲线,其实是以索引从0开始编号的
 *
 * @param yValues y值
 */
public void addEntry(LineData lineData, LineChart lineChart, float yValues, int index) {

    // 通过索引得到一条曲线,之后得到曲线上当前点的数量
    int xCount = lineData.getDataSetByIndex(index).getEntryCount();
    //添加时间数据
    timeDate.put(xCount, df.format(System.currentTimeMillis()));

    SimpleDateFormat df = new SimpleDateFormat("mm:ss");

    Entry entry = new Entry(lineData.getEntryCount(), yValues); // 创建一个点
    lineData.addEntry(entry, index); // 将entry添加到指定索引处的曲线中
    Log.d("LineChartDynamicActivity", "addEntry: "+entry.getX());
    //通知数据已经改变
    lineData.notifyDataChanged();
    lineChart.notifyDataSetChanged();

    //把yValues移到指定索引的位置
    lineChart.moveViewToAnimated(xCount - 4, yValues, YAxis.AxisDependency.LEFT, 1000);
    lineChart.invalidate();
}


/**
 * 功能:第1条曲线添加一个点
 */
public void addLine1Data(float yValues) {
    addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_1);
}

/**
 * 功能:第2条曲线添加一个点
 */
public void addLine2Data(float yValues) {
    addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_2);
}

/**
 * 功能:第3条曲线添加一个点
 */
public void addLine3Data(float yValues) {
    addEntry(mLineData, mLineChart, yValues, LINE_NUMBER_3);
}

/**
 * 功能:发送开始
 */
public void sendStartAddEntry() {
    if (!mDynamicHandler.hasMessages(MSG_START)) { // 判断是否有消息队列此消息,如果没有则发送
        mDynamicHandler.sendEmptyMessageDelayed(MSG_START, 1000);
    }
}

/**
 * 功能:暂停添加点,即移除所有消息
 */
public void sendPauseAddEntry() {
    mDynamicHandler.removeCallbacksAndMessages(null);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    // 清空消息
    mDynamicHandler.removeCallbacksAndMessages(null);
    mDynamicHandler = null;

    // moveViewToAnimated 移动到某个点,有内存泄漏,暂未修复,希望网友可以指着
    mLineChart.clearAllViewportJobs();
    mLineChart.removeAllViewsInLayout();
    mLineChart.removeAllViews();
}


@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.demo_start:
            sendStartAddEntry();
            break;
        case R.id.demo_pause:
            sendPauseAddEntry();
            break;
        case R.id.demo_checkbox1:
            showLine(LINE_NUMBER_1);
            break;
        case R.id.demo_checkbox2:
            showLine(LINE_NUMBER_2);
            break;
        case R.id.demo_checkbox3:
            showLine(LINE_NUMBER_3);
            break;
        default:
    }
 }
}  

里面调用了自定义MarkView,前面具有详线示例,可以参考使用,最后在onDestroy方法里清空消息。

上一篇下一篇

猜你喜欢

热点阅读