Android HelloChart 实现输入数据的实时曲线绘制,并保证曲线无卡滞
最近在忙一个蓝牙通信的项目,需要与下位机实时通信,并要实现在同一界面实现8个chart的同时绘制,通信频率为1秒/次。
一、图表选择
在项目之初,就预测到卡的问题,因此选择了HelloChart。参考文章:http://blog.****.net/u012534831/article/details/51505683。
二、android 布局
要实现一个界面八个图表的绘制,且x轴范围要求在10min,即600组数据,且一秒钟更新一次,因此选择FragmentPagerAdapter。
三、框架设计
首先由主Activity接受蓝牙数据,然后通过广播的形式发送给曲线绘制的ChartActivity,ChartActivity通过Handler发送数据给主线程,由主线程更新UI,函数为UpdateUI()。由于数据多,更新频率快,主线程任务过多,因此在主线程需要建立其他线程来辅助更新Chart。由于需要更新8个图表,我就大胆的尝试建立8个子线程。
由于是刚开始接触Android,走过很多弯路,开始天真的认为采用全局变量可以减少变量的循环赋值,因此很多都采用全局变量,后来发现,程序会卡的非常厉害,甚至莫名闪退。因此开始尝试局部变量,用完之后,直接设置为null,等待android垃圾回收。
四、代码
下面开始上代码:
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,否则不更新奥。