Android HelloChart 实现输入数据的实时曲线绘制,并保证曲线无卡滞

       最近在忙一个蓝牙通信的项目,需要与下位机实时通信,并要实现在同一界面实现8个chart的同时绘制,通信频率为1秒/次。

一、图表选择

       在项目之初,就预测到卡的问题,因此选择了HelloChart。参考文章:http://blog.csdn.net/u012534831/article/details/51505683。

二、android 布局

       要实现一个界面八个图表的绘制,且x轴范围要求在10min,即600组数据,且一秒钟更新一次,因此选择FragmentPagerAdapter。

三、框架设计

      首先由主Activity接受蓝牙数据,然后通过广播的形式发送给曲线绘制的ChartActivity,ChartActivity通过Handler发送数据给主线程,由主线程更新UI,函数为UpdateUI()。由于数据多,更新频率快,主线程任务过多,因此在主线程需要建立其他线程来辅助更新Chart。由于需要更新8个图表,我就大胆的尝试建立8个子线程。

      由于是刚开始接触Android,走过很多弯路,开始天真的认为采用全局变量可以减少变量的循环赋值,因此很多都采用全局变量,后来发现,程序会卡的非常厉害,甚至莫名闪退。因此开始尝试局部变量,用完之后,直接设置为null,等待android垃圾回收。

Android HelloChart 实现输入数据的实时曲线绘制,并保证曲线无卡滞

四、代码

     下面开始上代码:

4.1 全局变量的声明

ViewPager mViewPager;
ActionBar actionBar= null;
static int pageindex=0;
private static LineChartView lineChartView_O2,lineChartView_NO,lineChartView_NO2,lineChartView_NOx,lineChartView_SO2,lineChartView_CO,lineChartView_H2S,lineChartView_CO2;//ChartView

private static List<PointValue> pointValueListO2,pointValueListNO,pointValueListNO2,pointValueListNOx,pointValueListSO2,pointValueListCO,pointValueListCO2,pointValueListH2S;//点集

private static float mO2Max,mNOMax,mNO2Max,mNOxMax,mCOMax,mCO2Max,mH2SMax,mSO2Max=0;//点集的最小最大值
private static float mO2Min,mNOMin,mNO2Min,mNOxMin,mCOMin,mCO2Min,mH2SMin,mSO2Min=0;
private Thread O2Thread,NOThread,NO2Thread,NOxThread,SO2Thread,COThread,CO2Thread,H2SThread;//线程
Axis axisX = new Axis();
Axis axisY = new Axis().setHasLines(true);
private static int position = 0;
private int refreshRadio=2;//曲线的绘制频率


private static final int xRegion=600;//x轴的范围
private static final int xRegionMax=600;

4.2 界面更新,实现数据接受,转化,添加到点集,绘制曲线

private void UpdateUI(byte[] bytes) {//bytes为接受的byte数组
    byte[][] byte4=new byte[8][4];
    float[] tempFloat=new float[8];
    for(int i=0;i<8;i++)//实现从byte数据分割,并转化为float数组
    {
        System.arraycopy(bytes,dStart+i*4,byte4[i],0,4);
        if(i<9) {
            temp=AFactory.ArryToFloat(byte4[i], 0);
            tempFloat[i]= (float)(Math.round(temp*10))/10;//四舍五入保留一位有效数字
        }

    }
    //将新点添加到全局变量
    AndNewPointO2(tempFloat[0]);
    AndNewPointSO2(tempFloat[1]);
    AndNewPointNO(tempFloat[2]);
    AndNewPointNO2(tempFloat[3]);
    AndNewPointNOx(tempFloat[4]);
    AndNewPointCO(tempFloat[5]);
    AndNewPointH2S(tempFloat[6]);
    AndNewPointCO2(tempFloat[7]);

    if(position%refreshRadio==0) {
        StartThreads();
    }

     position++;

    byte4=null;
    tempFloat=null;
    show=null;
}
4.3 将新数据添加到点集

private void AndNewPointO2(float value) {//为O2曲线增加新点
    //实时添加新的点
    if (position > xRegionMax) {
        pointValueListO2.remove(0);
    }
    PointValue newpoint = new PointValue(position, value);
    pointValueListO2.add(newpoint);
    newpoint=null;
    if (position == 0) {
        mO2Max = value;
        mO2Min = value;
    }
    if (mO2Max < value)
        mO2Max = value;
    if (mO2Min > value)
        mO2Min = value;
}
4.4 开启线程

private void StartThreads()
{
    if(O2Thread==null) {
        O2Thread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart1();//即使线程没有处理完 也必须加载数据
            }
        });
        O2Thread.start();
    }
    if(NOThread==null) {
        NOThread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart2();
            }
        });
        NOThread.start();
    }
    if(NO2Thread==null) {
        NO2Thread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart3();
            }
        });
        NO2Thread.start();
    }
    if(NOxThread==null) {
        NOxThread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart4();
            }
        });
        NOxThread.start();
    }
    if(SO2Thread==null) {
        SO2Thread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart5();
            }
        });
        SO2Thread.start();
    }
    if(COThread==null) {
        COThread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart6();
            }
        });
        COThread.start();
    }
    if(CO2Thread==null) {
        CO2Thread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart7();
            }
        });
        CO2Thread.start();
    }
    if(H2SThread==null) {

        H2SThread = new Thread(new Runnable() {
            @Override
            public void run() {
                UpdateChart8();
            }
        });
        H2SThread.start();
    }
}
4.5 更新图表,实现将点集生成Line,再添加到Lines,然后添加到LineViewChart.

private void UpdateChart1() {

    synchronized (pointValueListO2) {
        LineChartData mO2=generateLineData(pointValueListO2,Color.GREEN);//将点集形成LineChartData


        if (lineChartView_O2 != null) {
            lineChartView_O2.setLineChartData(mO2);
            UpdateView(lineChartView_O2,mO2Min,mO2Max);
        }
        mO2=null;
    }
    O2Thread=null;
}
3.6 将点集形成LineChartData

private static LineChartData generateLineData(List<PointValue> list,int color)
{
    //根据新的点的集合画出新的线
    Line line = new Line(list);
    line.setStrokeWidth(1);
    //line.setPointRadius(2);
    line.setColor(color);
    //line.setShape(ValueShape.CIRCLE);
    line.setHasPoints(false);
    line.setCubic(true);//曲线是否平滑,即是曲线还是折线

    List<Line> lines = new ArrayList<Line>(1);
    lines.add(line);
    line=null;

    LineChartData data = new LineChartData(lines);
    lines=null;
    data.setAxisXBottom(new Axis().setName("时间:s"));
    data.setAxisYLeft(new Axis().setName("浓度").setHasLines(true));

    return data;
}

4.7 更新ViewPort

private static void UpdateView(LineChartView view,float min,float max){
    float range = (max-min)*10;
    int size=position;
    Viewport port;
    if ( size> xRegion) {
        port = initViewPort(max + range, min - range, size - xRegion, size);
    } else {
        port = initViewPort(max + range, min - range, 0, xRegion);
    }
    view.setMaximumViewport(port);
    view.setCurrentViewport(port);

}
3.8 Fragment类,记住,在添加完点集之后,一定要更新Viewport,否则不加载数据

public static class PlaceholderFragment extends Fragment {
    /**
     * The fragment argument representing the section number for this fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";

    public PlaceholderFragment() {
    }

    /**
     * Returns a new instance of this fragment for the given section number.
     */
    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView=null;
        RelativeLayout layout=null;
        int sectionNum = getArguments().getInt(ARG_SECTION_NUMBER);
        pageindex=sectionNum;
        switch (sectionNum) {
            case 1:
                rootView = inflater.inflate(R.layout.fragment_o2, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_O2=null;
                lineChartView_O2 = new LineChartView(getActivity());

                lineChartView_O2.setLineChartData(generateLineData(pointValueListO2,Color.GREEN));
                UpdateView(lineChartView_O2,mO2Min,mO2Max);

                lineChartView_O2.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_O2.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_O2);

                break;
            case 2:
                rootView = inflater.inflate(R.layout.fragment_no, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_NO=null;

                lineChartView_NO = new LineChartView(getActivity());

                lineChartView_NO.setLineChartData(generateLineData(pointValueListNO,Color.CYAN));
                UpdateView(lineChartView_NO,mNOMin,mNOMax);
                lineChartView_NO.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_NO.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_NO);


                break;
            case 3:
                rootView = inflater.inflate(R.layout.fragment_no2, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_NO2=null;
                lineChartView_NO2 = new LineChartView(getActivity());

                lineChartView_NO2.setLineChartData(generateLineData(pointValueListNO2,Color.YELLOW));
                UpdateView(lineChartView_NO2,mNO2Min,mNO2Max);
                lineChartView_NO2.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_NO2.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_NO2);


                break;
            case 4:
                rootView = inflater.inflate(R.layout.fragment_nox, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_NOx=null;
                lineChartView_NOx = new LineChartView(getActivity());

                lineChartView_NOx.setLineChartData(generateLineData(pointValueListNOx,Color.RED));
                UpdateView(lineChartView_NOx,mNOxMin,mNOxMax);
                lineChartView_NOx.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_NOx.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_NOx);

                break;
            case 5:
                rootView = inflater.inflate(R.layout.fragment_so2, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_SO2=null;
                lineChartView_SO2 = new LineChartView(getActivity());

                lineChartView_SO2.setLineChartData(generateLineData(pointValueListSO2,Color.BLUE));
                UpdateView(lineChartView_SO2,mSO2Min,mSO2Max);
                lineChartView_SO2.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_SO2.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_SO2);

                break;
            case 6:
                rootView = inflater.inflate(R.layout.fragment_co, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_CO=null;
                lineChartView_CO = new LineChartView(getActivity());

                lineChartView_CO.setLineChartData(generateLineData(pointValueListCO,Color.GRAY));
                UpdateView(lineChartView_CO,mCOMin,mCOMax);
                lineChartView_CO.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_CO.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_CO);

                break;
            case 7:
                rootView = inflater.inflate(R.layout.fragment_h2s, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_H2S=null;
                lineChartView_H2S = new LineChartView(getActivity());

                lineChartView_H2S.setLineChartData(generateLineData(pointValueListH2S,Color.DKGRAY));
                UpdateView(lineChartView_H2S,mH2SMin,mH2SMax);
                lineChartView_H2S.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_H2S.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_H2S);

                break;
            case 8:
                rootView = inflater.inflate(R.layout.fragment_co2, container, false);
                layout = (RelativeLayout) rootView;
                lineChartView_CO2=null;
                lineChartView_CO2 = new LineChartView(getActivity());

                lineChartView_CO2.setLineChartData(generateLineData(pointValueListCO2,Color.MAGENTA));
                UpdateView(lineChartView_CO2,mCO2Min,mCO2Max);
                lineChartView_CO2.setZoomType(ZoomType.HORIZONTAL);

                /** Note: Chart is within ViewPager so enable container scroll mode. **/
                lineChartView_CO2.setContainerScrollEnabled(true, ContainerScrollType.HORIZONTAL);

                layout.addView(lineChartView_CO2);

                break;
        }

        return rootView;
    }
}

五、总结
   5.1 尽量少用全局变量,局部变量可以实时回收,有利于实时释放内存;
   5.2 ViewPage加载更新数据的时候,一定更要更新ViewPort,否则不更新奥。