Android使用achartengine绘制折线图linechart

01 需求

最近项目需求需要接收数据,绘制折线图,具体需求如下:

  • 双Y轴,其中一个可以动态调节y轴刻度,另外一个刻度固定
  • 显示两条折线
  • X轴显示时间
  • 动态的实时的展示数据

最后的效果如下图所示

android achartengine

02 技术选型

A 自定义view  实现绘制曲线, 实时刷新, y轴自适应, 多曲线绘制, 工期紧,难度略大

B 使用开源组件:  

本项目选择的是AchartEngine  直接导入jar包,版本如下, Android studio版本  3.1.2

    implementation files('libs/achartengine-1.2.0.jar')

 

03 实现

代码的实现参考了github项目中的demo代码,MultipleTemperatureChart.java

双Y轴的实现要点:

  • 自定义继承XYSeries的类要添加一个包含scaleNumber的构造函数
  • 如果绘制多条曲线,比如两条,render要有两个,dataset也要有两个,其中一个label的align设置位于左边,另外一个设置在右边
  • 设置不同曲线对应的的render时,要用参数scale来区分设置的曲线对应哪个dataset,getDemoRenderer()函数中有很多代码用到了scale参数。设置dataset的时候也用到了scaleNumber参数,如getDateDemoDataset()中创建series和seriesPro的代码。
  • render的数量和dataset数量要保持一致否则会抛出异常"Dataset and renderer should be not null and should have the same number of series"

主要代码:

A 自定义TimeSeries

package com.jdd.net.bledemo.ui.achartengine_custom;

import org.achartengine.model.XYSeries;

import java.util.Date;

public class TimeSeries extends XYSeries {

    public TimeSeries(String title) {
        super(title);
    }

    public TimeSeries(String title, int scaleNumber) {
        super(title, scaleNumber);//比原来的TimeSeries加了此函数
    }

    public synchronized void add(Date x, double y) {
        super.add((double)x.getTime(), y);
    }

    protected double getPadding(double x) {
        return 1.0D;
    }
}

主要的Activity代码(有些蓝牙连接的代码可以忽略,主要看 render 和dataset部分)

package com.jdd.net.bledemo;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.os.Bundle;



import android.app.Activity;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;

import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.PathUtils;
import com.jdd.net.bledemo.receiver.BluetoothReceiverSimple;
import com.jdd.net.bledemo.ui.achartengine_custom.TimeSeries;
import com.jdd.net.bledemo.ui.chart.MultipleTemperatureChart;
import com.jdd.net.bledemo.utils.Constant;
import com.jdd.net.bledemo.utils.Tools;

import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.CubicLineChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.XYChart;

import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;

import top.wuhaojie.bthelper.BtHelperClient;


/**
 * @author hao
 *
 * 协议版本   蓝牙2.0   默认的串口通信  ssp uuid是默认的
 * 00001101-0000-1000-8000-00805f9b34fb
 * 00001101-0000-1000-8000-00805F9B34FB
 *
 *
 *
 *
 *
 */



public class ClientActivity extends Activity implements OnClickListener{
    private static final String TAG = "ClientActivity";

    /** Called when the activity is first created. */
    private Button autopairbtn=null;
    private Button btnReadData;
    private Button btnCloseCon;  //断开蓝牙连接的按钮
    private TextView  tvWordLenTips;   //统计记录的长度
    private TextView  tvFilePath;   //文件的地址
    private BluetoothAdapter bluetoothAdapter;
    //获取已经配对过的设备的集合
    Set<BluetoothDevice> bondedDevices;

    private static final int REQUEST_PERMISSION_ACCESS_LOCATION = 555;


    private  String MY_UUID ;
    private final String SSP_UUID = "00001101-0000-1000-8000-00805F9B34FB";

    public BtHelperClient  btHelperClient;

    private BluetoothSocket mClientBleSocket;

    clientThread estabThread;  //建立socket连接
    readThread mreadThread;   // 读取数据的链接
    public volatile boolean isReading = true;


    public String  data_text_p1 = "";
    private Button btnWrite ;

    private long startTime;
    private boolean firstFlag = true;

    private TextView tv_timer;


    private EditText  etShowContent;
    private  int readCount = 0;


    //==============data for line chart  start==============

    int constNum = 100;
    int unit = 1;
    private Timer timer = new Timer();
    private GraphicalView chart;
    private TimerTask task;
    private int addY = -1;
    private long addX;
    private TimeSeries series;  //实时数据对应的series,使用自定义数据
    private TimeSeries seriesPro;  //处理后的数据对应的坐标
    private XYMultipleSeriesDataset dataset;
    private Handler handler;
    private Random random=new Random();

    Date[] xcache = new Date[constNum];
    int[] ycache = new int[constNum];


    Date[] xcachePro = new Date[constNum];
    int[] ycachePro = new int[constNum];

    //==============data fo人 line chart  end==============



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        requestPermission();
        registerBroadcast();

        initBle();
        initViews();
        initChart();




    }


    private void initChart(){


        LinearLayout layout1 = findViewById(R.id.ll_chart_zone);
        //生成图表
        chart = ChartFactory.getTimeChartView(this, getDateDemoDataset(), getDemoRenderer(), "hh:mm:ss");
        //layout1.addView(chart, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,580));
        //layout1.addView(chart, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,780));
        layout1.addView(chart, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));


//        MultipleTemperatureChart dChart = new MultipleTemperatureChart();
//        XYChart chartNew = dChart.getDoubleLineChart(this);
//        GraphicalView chart2 = new GraphicalView(this, chartNew);
//        layout1.addView(chart2, new ViewGroup.LayoutParams(1500,580));


        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                //刷新图表
                addY=random.nextInt()%5+10 + unit;

                        unit++;

                        if (unit >= 1000){
                            unit = 100;
                        }

                updateChart(addY);
                super.handleMessage(msg);
            }
        };
        task = new TimerTask() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = 200;
                handler.sendMessage(message);
            }
        };
        timer.schedule(task, 2*1000,200);     //20 / 50Hz


    }



    private void initViews(){

        autopairbtn=  findViewById(R.id.btn_match);
        btnReadData=  findViewById(R.id.btn_read_data);
        btnCloseCon=  findViewById(R.id.btn_close_device);
        tvWordLenTips=  findViewById(R.id.tv_word_len_tips);
        tvFilePath=  findViewById(R.id.tv_file_path);
        tv_timer = findViewById(R.id.tv_timer);
        etShowContent = findViewById(R.id.et_show_content);
        //btnWrite=  findViewById(R.id.btn_write);


        /*btnWrite.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                createFile(System.currentTimeMillis());
            }
        });
*/
        btnCloseCon.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                if (mreadThread != null){
                    mreadThread.cancel();
                    isReading = false;
                    Log.d(TAG, "onClick: 结束读取数据的线程");
                    createFile(System.currentTimeMillis());

                }

            }
        });

        btnReadData.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                /**
                 * 如果已经进行了连接,那么搜索操作将会显著地降低连接的速率,因此你应当在连接时停止搜索。
                 * 可通过cancelDiscovery()方法停止搜索。
                 */
                bluetoothAdapter.cancelDiscovery();  //停止搜索

                //从已经配对的设备中找到目标设备
                BluetoothDevice device =  findBondedDevice();

                if (device != null){
                    Log.d(TAG, "onClick: device name is " + device.getName());
                    Toast.makeText(ClientActivity.this, "设备名称--->"+ device.getName(), Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(ClientActivity.this, "没有找到配对设备", Toast.LENGTH_SHORT).show();
                    return;
                }
                //建立连接
                establishConn(device);

                //读取数据



            }
        });

        autopairbtn.setOnClickListener(this);

    }





    //设置按钮的监听方法
    //自动配对的方法
    @Override
    public void onClick(View arg0) {

        if (!bluetoothAdapter.isEnabled())
        {
            bluetoothAdapter.enable();//异步的,不会等待结果,直接返回。
        }else{
            //开始搜索设备
            //来开始广播。当广播的事件是我们刚刚注册的事件时就会触发广播接收器,并且触发广播接收器中的onReceiver()方法。
            //该搜索的过程调用12秒钟的查询,随后返回找到的设备。
            bluetoothAdapter.startDiscovery();
            Toast.makeText(this, "开始搜索设备", Toast.LENGTH_SHORT).show();
        }

    }

    /**
     * 在destory方法中需要解除注册
     */
    private void registerBroadcast(){
        Log.d(TAG, "registerBroadcast: ");
        //注册广播
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        registerReceiver(new BluetoothReceiverSimple(), intentFilter);
    }

    private void initBle(){


        //MY_UUID is the app's UUID string, also used by the server code
        MY_UUID = UUID.randomUUID().toString();
        Log.d(TAG, "initBle: 生成的UUID是"  + MY_UUID );

        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        bondedDevices = bluetoothAdapter.getBondedDevices();
        int size = bondedDevices.size();
        Log.d(TAG, "initBle: 绑定设备的数量---->" + size);


        //注册服务
        btHelperClient = BtHelperClient.from(ClientActivity.this);



    }

    //申请权限
    private void requestPermission() {
        if (Build.VERSION.SDK_INT >= 23) {
            int checkAccessFinePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
            if (checkAccessFinePermission != PackageManager.PERMISSION_GRANTED) {


                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        REQUEST_PERMISSION_ACCESS_LOCATION);
                Log.e(getPackageName(), "没有权限,请求权限");
                return;
            }
            Log.e(getPackageName(), "已有定位权限");
            //这里可以开始搜索操作
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION_ACCESS_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.e(getPackageName(), "开启权限permission granted!");
                    //这里可以开始搜索操作
                } else {
                    Log.e(getPackageName(), "没有定位权限,请先开启!");
                }
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }


    private BluetoothDevice findBondedDevice(){
        BluetoothDevice device = null;

        bondedDevices = bluetoothAdapter.getBondedDevices();
        Log.d(TAG, "findBondedDevice: 当前绑定设备的数量是-->" +  bondedDevices.size());

        if (bondedDevices!= null){
            for (BluetoothDevice item :
                    bondedDevices) {
                String name = item.getName();
                if (name != null && name.contains(Constant.DEVICE_NAME)){
                    device = item;
                    //获取列表中的第一个匹配的设备
                    break;
                }

            }
        }
        return device;

    }



    private void establishConn(BluetoothDevice device
                               ){

        bluetoothAdapter.cancelDiscovery();
        estabThread = new clientThread(device);
        estabThread.start();   //开启线程

    }


    /**
     * 客户端连接蓝牙串口设备的线程
     * 费时的过程放到线程中执行
     *
     */


    /**
     * 读取数据
     */
    private class readThread extends Thread {

        private BluetoothSocket clientSocket;
        //private final InputStream mmInStream;


        public readThread(BluetoothSocket socket){
            clientSocket = socket;
            /*InputStream tmpIn = null;
            try {
                tmpIn = socket.getInputStream();
                show("客户端:获得输入流");

                //tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }*/
            //mmInStream = tmpIn;
            //mmOutStream = tmpOut;
        }


        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;
            InputStream is = null;
            try {
                is = clientSocket.getInputStream();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            while (isReading) {
                try {
                    if ((bytes = is.read(buffer)) > 0) {
                        byte[] buf_data = new byte[bytes];
                        for (int i = 0; i < bytes; i++) {
                            buf_data[i] = buffer[i];
                        }
                        String s = Tools.bytesToHexString(buf_data);
                        int num = -1;
                        try{
                            num = Integer.parseInt(Tools.HexToTenSpecialBle(Tools.cutString(s)),16);

                            num = num / 10000;

                        }catch (Exception e){
                            //
                            num = -1;
                        }

                        if (firstFlag ){
                            startTime = System.currentTimeMillis();
                            //dataTimer.start();
                            firstFlag = false;
                        }
                        //updateChart(num);
                        readCount++;
                        if (readCount >= 25){
                            readCount = 0;
                            if (num >=0){
                                //updateChart(num);
                            }
                            show("客户端:读取数据了" + num + "#\n");


                        }

//
                        //recordData(  s);

                    }else{
                        Log.d(TAG, "run: 获取的数据为空");
                    }
                } catch (IOException e) {
                    try {
                        Log.d(TAG, "run: 出现异常");
                        is.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    break;
                }

                /*try {
                    Scanner in = new Scanner(mmInStream,"UTF-8");
                    String str = in.nextLine();
                    Log.i(TAG,"read: "+str);
                } catch (Exception e) {
                    try {
                        Log.d(TAG, "run: 出现异常");
                        mmInStream.close();
                    } catch (IOException e1) {
                        e1.printStackTrace();
                    }
                    break;
                }*/


            }
        }

        public void cancel(){
            try{
                clientSocket.close();
                Log.d(TAG, "读取数据的线程   cancel: 关闭蓝牙socket连接");
            }catch (IOException e){
                //
            }
        }



    }


    /**
     * 开启客户端,简历客户端socket连接
     */
    private class clientThread extends Thread {

        BluetoothDevice device;
        BluetoothSocket socket;

        public clientThread(BluetoothDevice de
                            ){
            device = de;
            //socket = clientSocket;

            BluetoothSocket tmp = null;
            try {
                //尝试建立安全的连接
                tmp = de.createRfcommSocketToServiceRecord(UUID.fromString(SSP_UUID));
                //尝试建立不安全的连接
                //tmp = mmDevice.createInsecureRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
                Log.i(TAG,"获取 BluetoothSocket失败");
                e.printStackTrace();
            }
            socket = tmp;


        }


        public void run() {
            try {
                if(bluetoothAdapter.isDiscovering()){
                    bluetoothAdapter.cancelDiscovery();
                }


                //连接
                show("客户端:开始连接...");
                //  由于connect()为阻塞调用,因此该连接过程应始终在主 Activity 线程以外的线程中执行


                socket.connect();


                show("客户端:连接成功");
                //启动接受数据

            } catch (IOException e) {
                show("客户端:连接服务端异常!断开连接重新试一试");
                e.printStackTrace();
            }

            show("客户端:启动接受数据");
            mreadThread = new readThread(socket);
            mreadThread.start();



        }

        public void cancel(){
            try{
                socket.close();
            }catch (IOException e){
                //
            }
        }

    }


    private void show(String msg){
        Log.d(TAG, "show: msg:--->" + msg + "\n");



    }

    private void recordData(String msg){

        data_text_p1 = data_text_p1 +
                //"nSpo2:" + nSpO2 +"\n\r" +
                //"nPR:" + nPR +"\n\r" +
                (System.currentTimeMillis()- startTime) +
                "," +
                msg +"\n" ;


    }


    //创建文本文件

    /**
     * 需要将txt文本和视频文件保存在同一个目录中
     */
    private void createFile(Long timeGen){
        Log.d(TAG, "写入数据到文件,createFile: start");
        String str0 = data_text_p1 ;


        //获取外村应用文件数据路径
        String fileDataPath = PathUtils.getExternalAppDataPath();
        String appPath = PathUtils.getExternalStoragePath();
        //Log.d(TAG, "createFile: 外存应用数据路径-->" + fileDataPath);
        Log.d(TAG, "createFile: 外存应用数据路径-->" + appPath);

        String folderPath = appPath + File.separator +
                "bluetooth"  +
                File.separator +
                "data"

                ;

        String path = folderPath +
                File.separator +
                "data" + "_" +
                getCurrentTimeNew(timeGen) + "_0" +
                ".txt";


        String showText = "";
        if (TextUtils.isEmpty(str0)){
            showText = "文件的长度是:0 " +   ",文件空" ;
        }else{
            int wordLen = str0.length();

            if (wordLen >=1000){
                showText = "文件长度是:" + wordLen + "" + str0.substring(0,999);
                etShowContent.setText(str0.substring(0,999));

            }else{
                showText = "文件的长度是:" + wordLen + "" ;
            }
        }


        tvWordLenTips.setText(showText);

        Log.d(TAG, "createFile: 创建的文件是-->" + path);
        tvFilePath.setText("文件本地路径:" + path);

        //createOrExistsDir         : 判断目录是否存在,不存在则判断是否创建成功
        //createOrExistsFile        : 判断文件是否存在,不存在则判断是否创建成功
        //创建文件夹

        boolean dirFlag =  FileUtils.createOrExistsDir(folderPath);
        Log.d(TAG, "createFile: create dir result is-->" + dirFlag);

        //创建文件
        boolean fileFlag = FileUtils.createOrExistsFile(path);
        Log.d(TAG, "createFile: create file result is-->" + fileFlag);


        //String txt = "2019-07-11 22:19:17.281 23091-23091/com.creative.libdemo D/MainActivity: createFile: 创建的文件是\n" +
        //       "-->/storage/emulated/0/Android/data/com.creative.libdemo/dataRecordDemo/ble_data.txt";
        writeStringToFile(path,str0);

    }


    private void writeStringToFile(String filePath, String str){
        //writeFileFromString        : 将字符串写入文件

        boolean result =  FileIOUtils.writeFileFromString(filePath,str);
        Log.d(TAG, "writeStringToFile: 写入结果-->" + result);
    }


    private String getCurrentTimeNew(long timeMillis){
        SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd_HHmmss_SSS");
        Date date = new Date(timeMillis);
        String dateStr = formatter.format(date);
        return dateStr;
    }



    private CountDownTimer dataTimer = new CountDownTimer(10*1000,1000) {
        @Override
        public void onTick(long millisUntilFinished) {
            //剩余的秒数,设置更新剩余的描述
            tv_timer.setVisibility(View.VISIBLE);
            tv_timer.setText("" + millisUntilFinished/1000 + "秒后结束采集");

            }

        @Override
        public void onFinish() {


            if (mreadThread != null){
                mreadThread.cancel();
                isReading = false;
                Log.d(TAG, "onClick: 结束读取数据的线程");
                createFile(System.currentTimeMillis());

            }

        }
    };



    //  =================绘图函数  start ===================
    private void updateChart(int tempY) {
        //设定长度为20
        int length = series.getItemCount();
        int lengthPro = seriesPro.getItemCount();
        if(length>=constNum) length = constNum;
        //addY=random.nextInt()%5+10;
        addY=tempY;
        addX=new Date().getTime();



        //将前面的点放入缓存
        for (int i = 0; i < length; i++) {
            xcache[i] =  new Date((long)series.getX(i));
            ycache[i] = (int) series.getY(i);

            xcachePro[i] =  new Date((long)seriesPro.getX(i));
            ycachePro[i] = (int) seriesPro.getY(i);
        }


        series.clear();
        seriesPro.clear();
        //将新产生的点首先加入到点集中,然后在循环体中将坐标变换后的一系列点都重新加入到点集中
        series.add(new Date(addX), addY);
        seriesPro.add(new Date(addX), addY- unit);
        for (int k = 0; k < length; k++) {
            series.add(xcache[k], ycache[k]);
            seriesPro.add(xcachePro[k], ycachePro[k]);
        }
        //在数据集中添加新的点集
        dataset.removeSeries(series);
        dataset.removeSeries(seriesPro);
        dataset.addSeries(series);
        dataset.addSeries(seriesPro);
        //曲线更新
        chart.invalidate();
    }
    private XYMultipleSeriesRenderer getDemoRenderer() {
        XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer(2);


        renderer.setPointSize(4.5f);
        int[] colors = new int[] { Color.RED, Color.BLUE };

        //先添加孩子render


        XYSeriesRenderer r1 = new XYSeriesRenderer();
        r1.setColor(Color.RED);
        r1.setChartValuesTextSize(15);
        r1.setChartValuesSpacing(6);
        r1.setPointStyle(PointStyle.POINT);
        r1.setFillBelowLine(true);
        r1.setFillBelowLineColor(Color.TRANSPARENT);
        r1.setFillPoints(true);

        XYSeriesRenderer r2 = new XYSeriesRenderer();
        r2.setColor(Color.BLUE);
        r2.setChartValuesTextSize(15);
        r2.setChartValuesSpacing(6);
        r2.setPointStyle(PointStyle.POINT);
        r2.setFillBelowLine(true);
        r2.setFillBelowLineColor(Color.TRANSPARENT);
        r2.setFillPoints(true);



        renderer.addSeriesRenderer(r1);
        renderer.addSeriesRenderer(r2);




        //再设置线的宽度
        int length = renderer.getSeriesRendererCount();
        for (int i = 0; i < length; i++) {
            XYSeriesRenderer r = (XYSeriesRenderer) renderer.getSeriesRendererAt(i);
            r.setLineWidth(4f);  //折线图线条的宽度
            r.setFillPoints(true);
        }

        //设置主坐标轴,默认主坐标轴是左侧的Y轴
        renderer.setChartTitle("信号图");//标题
        renderer.setChartTitleTextSize(60);
        renderer.setXTitle("时间");    //x轴说明
        renderer.setYTitle("原始数据");
        renderer.setAxisTitleTextSize(35);
        renderer.setAxesColor(Color.BLACK);
        renderer.setLabelsTextSize(18);    //数轴刻度字体大小
        renderer.setLabelsColor(Color.BLACK);

        renderer.setLegendTextSize(38);    //曲线说明
        renderer.setXLabelsColor(Color.BLACK);
        renderer.setShowLegend(true);
        renderer.setMargins(new int[] {30, 60, 35, 30});//上左下右{ 20, 30, 100, 0 })

        renderer.setShowGrid(true);
        renderer.setXLabelsAlign(Paint.Align.RIGHT);
        renderer.setYLabelsAlign(Paint.Align.RIGHT);

        renderer.setMarginsColor(Color.WHITE);
        renderer.setPanEnabled(false,false);
        renderer.setShowGrid(true);
        //renderer.setYAxisMax(20);//纵坐标最大值
        //renderer.setYAxisMin(0);//纵坐标最小值
        renderer.setInScroll(false);


        renderer.setYAxisMax(30,1);
        renderer.setYAxisMin(0,1);

        //设置第二组数据的Y轴


        renderer.setYLabelsColor(0, colors[0]);
        renderer.setYLabelsColor(1, colors[1]);

        renderer.setYTitle("处理后数据",1);
        renderer.setYLabelsColor(1,colors[1]);
        renderer.setYLabelsColor(0,colors[0]);
        renderer.setYAxisAlign(Paint.Align.RIGHT, 1);
        renderer.setYLabelsAlign(Paint.Align.RIGHT, 1);
        renderer.setGridColor(colors[0], 0);
        renderer.setGridColor(colors[1], 1);



        return renderer;
    }
    private XYMultipleSeriesDataset getDateDemoDataset() {//初始化的数据
        dataset = new XYMultipleSeriesDataset();
        final int nr = 10;
        long value = new Date().getTime();
        Random r = new Random();
        series = new TimeSeries("原始数据 " ,0);
        seriesPro = new TimeSeries("处理后数据 " ,1);
        for (int k = 0; k < nr; k++) {
            series.add(new Date(value+k*1000), 20 +r.nextInt() % 10);//初值Y轴以20为中心,X轴初值范围再次定义
            seriesPro.add(new Date(value+k*1000), 20 +r.nextInt() % 10);//初值Y轴以20为中心,X轴初值范围再次定义
        }
        dataset.addSeries(series);
        dataset.addSeries(seriesPro);
        return dataset;
    }
    @Override
    public void onDestroy() {
        //当结束程序时关掉Timer
        timer.cancel();
        super.onDestroy();
    }

    //  =================绘图函数  end ===================





}

求完整的项目的小伙伴请留言。

 

04 参考 

A 七款Android开发者常用UI组件  

 

 

 

 

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值