美文网首页
MPAndroidChart(3)曲线图LineChart(多条

MPAndroidChart(3)曲线图LineChart(多条

作者: 古早味蛋糕 | 来源:发表于2022-10-27 18:56 被阅读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方法里清空消息。

    相关文章

      网友评论

          本文标题:MPAndroidChart(3)曲线图LineChart(多条

          本文链接:https://www.haomeiwen.com/subject/hioozrtx.html