在android中实现动态跑动的图表实现方法

仿ES界面写的文件浏览器
http://www.eoeandroid.com/thread-226511-1-1.html

Android 新版捕鱼达人源码
http://www.eoeandroid.com/thread-197437-1-1.html

Android的学习初体验猜牌游戏源码
http://www.eoeandroid.com/thread-163542-1-1.html

 

实现这个的目的是为了在手机屏幕上能够动态显示接收的数据,适合哪种不断有新数据接收时需要时时展现的程序。
首先我们采用的实现方式是:SurfaceView的onDraw实现,初始化一个view,画已有的点,提供添加点的接口给它的Activity调用,onDraw方法要实现根据点的数组画点。
首先我利用了以前写的一个游戏的VIew它实现了不间断的调用onDraw,这样才能保证点是一直跑动的,代码:

  
import android.content.Context; 
import android.graphics.Canvas; 
import android.util.AttributeSet; 
import android.util.Log; 
import android.view.KeyEvent; 
import android.view.MotionEvent; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
/** 
 *  
 * Copyright (c) 2012 All rights reserved 
 * 名称:GameView.java  
 * 描述:继承自该类的view只需要考虑怎么绘制画面 
 * 需要实现draw(Canvas canvas)方法 
 * 注意:实现的draw方法中不要锁定、解锁画布 
 * 不要进行异常处理 
 * @author zhaoqp 
 * @date:2012-10-30 下午4:42:32 
 * @version v1.0 
 */ 
public abstract class GameView extends SurfaceView implements SurfaceHolder.Callback{ 
  
 private static final String TAG = "GameView"; 
 public ViewThread thread;   //刷帧的线程 
 //定义SurfaceHolder对象 
 private SurfaceHolder mSurfaceHolder = null; 
 public String fps="FPS:N/A";          //用于显示帧速率的字符串,调试使用 
 private boolean loop = true; 
 private boolean pause = true; 
 //睡眠的毫秒数  
 private int sleepSpan = 100; 
 public GameView(Context context){ 
  super(context); 
  init(); 
 } 
  
 public GameView(Context context, AttributeSet attrs, int defStyle) { 
  super(context, attrs, defStyle); 
  init(); 
 } 
 public GameView(Context context, AttributeSet attrs) { 
  super(context, attrs); 
  init(); 
 } 
  
 public void init(){ 
  //Log.d(TAG, "--GameView Created--"); 
  // 实例化SurfaceHolder 
  mSurfaceHolder = this.getHolder(); 
  // 添加回调 
  mSurfaceHolder.addCallback(this); 
  this.setFocusable(true); 
  thread = new ViewThread(mSurfaceHolder,this); 
  thread.start(); 
 } 

 /** 
  * 设置刷新的sleep间隔时间 
  */ 
 public void setSleep(int time){ 
  this.sleepSpan = time; 
 } 
  
 /** 
     * 设置循环标记位 
     * @param loop 
     */ 
    public void setLoop(boolean loop) { 
     this.loop = loop; 
    } 
     
   /** 
    * 设置循环暂停标记位 
    * @param pause 
    */ 
   public void setPause(boolean pause) { 
        this.pause = pause; 
   } 
  
 /** 
  * 绘图 
  */ 
 public abstract void onDraw(Canvas canvas); 
  
 /** 
  * 在surface创建时激发的扩展方法 
  */ 
 public void expandSurfaceCreated(){ 
 } 
 /** 
  * 在surface创建时激发的扩展方法 
  */ 
 public void expandSurfaceDestroyed(){ 
 } 
  
 /** 
  * 在surface的大小发生改变时激发 
  */ 
 @Override 
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){ 
 } 
 /** 
  * 在surface销毁时激发 
  */ 
 @Override 
 public void surfaceCreated(SurfaceHolder holder){ 
   //如果后台重绘线程没起来,就启动它 
   if(! this.thread.isAlive()){ 
          try{ 
           //启动刷帧线程 
           this.setLoop(true); 
           this.setPause(true); 
           this.thread.start(); 
           expandSurfaceCreated(); 
          }catch(Exception e){ 
           e.printStackTrace(); 
           this.setLoop(false); 
           this.setPause(false); 
          }          
         } 
  Log.d(TAG, "--surfaceCreated--"); 
 } 
 /** 
  * 在surface销毁时激发 
  */ 
 @Override 
 public void surfaceDestroyed(SurfaceHolder holder){ 
        releaseViewThread(); 
        Log.d(TAG, "--surfaceDestroyed--"); 
 } 
  
 /** 
  * 释放view类线程 
  */ 
 public void releaseViewThread(){ 
  if(thread != null && thread.isAlive()){ 
   this.setPause(false); 
   this.setLoop(false); 
    try { 
                 thread.interrupt(); 
          }catch (Exception e) { 
              e.printStackTrace(); 
          } 
  } 
  thread = null; 
 } 
  
 /** 
  * 触摸屏事件 
  * @param event 
  */ 
 public void onMyTouchEvent(MotionEvent event){ 
 } 
  
 /** 
  * 按键事件按下 
  * @param keyCode 
  * @param event 
  */ 
 public void onMyKeyDown(int keyCode, KeyEvent event) { 
 } 
  
 /** 
  * 按键事件抬起 
  * @param keyCode 
  * @param event 
  */ 
 public void onMyKeyUp(int keyCode, KeyEvent event) { 
 } 
  
    /** 
     * 滚动事件 
     * @param event 
     */ 
 public void onMyTrackballEvent(MotionEvent event) { 
 } 
  
 /** 
  * 刷帧线程 
  */ 
 class ViewThread extends Thread{ 
   
  private SurfaceHolder surfaceHolder; 
  private GameView gameView; 
  private int count = 0;              //记录帧数,该变量用于计算帧速率 
  private long start = System.nanoTime(); //记录起始时间,该变量用于计算帧速率 
  /** 
   * 构造方法 
   * @param surfaceHolder 
   * @param gameView 
   */ 
        public ViewThread(SurfaceHolder surfaceHolder, GameView gameView) { 
            this.surfaceHolder = surfaceHolder; 
            this.gameView = gameView; 
        } 
         
  @Override 
  public void run() { 
   Canvas canvas; 
            while (loop) { 
               while (pause) { 
             canvas = null; 
                try { 
                 if(!Thread.currentThread().isInterrupted()){ 
                  //锁定整个画布,在内存要求比较高的情况下,建议参数不要为null 
                  canvas = this.surfaceHolder.lockCanvas(); 
                        synchronized (this.surfaceHolder) { 
                         gameView.onDraw(canvas);//绘制 
                        } 
                 } 
                }catch (Exception e){ 
        e.printStackTrace(); 
        Thread.currentThread().interrupt(); 
        loop = false; 
       }finally { 
                    if (canvas != null) { 
                     //更新屏幕显示内容 
                        this.surfaceHolder.unlockCanvasAndPost(canvas); 
                    } 
                } 
       this.count++; 
       if(count == 20){ //如果计满20帧 
        count = 0;  //清空计数器 
        long tempStamp = System.nanoTime(); //获取当前时间 
        long span = tempStamp - start;  //获取时间间隔 
        start = tempStamp;     //为start重新赋值 
        double fps = Math.round(100000000000.0/span*20)/100.0;//计算帧速率 
        gameView.fps = "FPS:"+fps;//将计算出的帧速率设置到gameView的相应字符串对象中 
       } 
                try{ 
                 Thread.sleep(sleepSpan);  //睡眠指定毫秒数 
                }catch(Exception e){ 
                 e.printStackTrace();      //打印堆栈信息 
                } 
             } 
    } 
    } 
 } 
}

接下来实现这个View

这个View要实现能够根据数组中的点时时绘制,其中x轴每个单位的间距我设置为50,通过offect实现点的慢移动,代码:
  
public class VisualizerView extends GameView { 
 private float[] mPoints; 
 private float[] mPointsDraw; 
 // 画笔 
 private Paint mPaint = new Paint(); 
 private int offect = 0; 
 // 初始化画笔 
 private void init1() { 
  mPaint.setAntiAlias(true); 
  mPaint.setFakeBoldText(true); 
  mPaint.setTextSize(13); 
 } 
 public VisualizerView(Context context) { 
  super(context); 
  init1(); 
 } 
 public VisualizerView(Context context, AttributeSet attr) { 
  super(context, attr); 
  init1(); 
 } 
 public void updateVisualizer(float[] points) { 
   if(points != null){ 
    this.mPoints = points; 
    mPointsDraw = new float[points.length-1]; 
    System.out.println("新点旧加入"); 
    offect = 0; 
   } 
   //一个点进来  要执行多次onDraw,将点缓缓移动 
   //移动50次 
   //invalidate(); 
 } 
 @Override 
 public void onDraw(Canvas canvas) { 
  canvas.drawColor(Color.WHITE); 
  mPaint.setStrokeWidth(0.2f); 
  //底线 
  mPaint.setColor(Color.RED); 
  //canvas.drawLine(0, getHeight(), getWidth(), getHeight(), mPaint); 
  //顶线 
  mPaint.setColor(Color.RED); 
  //canvas.drawLine(0, 0, getWidth(), 0, mPaint); 
  //中线 
  mPaint.setColor(Color.GRAY); 
  canvas.drawLine(0, getHeight()/2, getWidth(),getHeight()/2, mPaint); 
  //每个单位20大小 
  //99 
  mPaint.setColor(Color.RED); 
  canvas.drawLine(0, getHeight()/2+40,getWidth(),getHeight()/2+40, mPaint); 
  //95 
  canvas.drawLine(0, getHeight()/2-40,getWidth(),getHeight()/2-40, mPaint); 
   
  mPaint.setColor(Color.GRAY); 
  canvas.drawText("99", 0, getHeight()/2-42, mPaint); 
  canvas.drawText("95", 0, getHeight()/2+40, mPaint); 
   
  if (mPoints != null && mPoints.length>1) { 
   mPointsDraw = new float[99]; 
   //把中线认为是97,计算点的偏移 
   for (int i = 1; i < mPoints.length; i++) { 
    if(mPoints[i]!=0){ 
     float offect = mPoints[i]-97; 
     offect = offect*20; 
     mPointsDraw[i-1] = getHeight()/2-offect; 
    }else{ 
     mPointsDraw[i-1] = 97; 
    } 
   } 
    
   mPaint.setStrokeWidth(1f); 
   //最后结果 
   for (int i = 0; i < mPointsDraw.length; i++) { 
    mPaint.setColor(Color.GRAY); 
    canvas.drawCircle(50*i-offect,mPointsDraw[i], 2, mPaint); 
    canvas.drawText(String.valueOf((int)mPoints[i+1]), (float)50*i-offect-10, mPointsDraw[i]-3, mPaint); 
    mPaint.setColor(Color.BLUE); 
    canvas.drawLine(50*i-offect, mPointsDraw[i], 50*(i+1)-offect,mPointsDraw[i], mPaint); 
   } 
    
   if(offect<50){ 
    offect+=10; 
   } 
    
  } 
 } 
  
}

首先要在顶层布局中写上view的schemas: 

<com.chxue8.view.VisualizerView           
android:id="@+id/mVisualizerView" 
android:layout_width="400dip" 
 android:layout_height="200dip" 
 android:layout_marginTop="40dip" 
 android:layout_marginLeft="20dip" 
             > 
</com.chxue8.view.VisualizerView>
  
  VisualizerView mVisualizerView = (VisualizerView) findViewById(R.id.mVisualizerView); 
  float[] pointFloat = null; 
  pointFloat = new float[100]; 
  pointFloat[0] = 0;//数组第一位哨兵 保存当前大小 
  pointFloat[1] = 97; //预留的值 
 

当有新数据过来

  
 int size  = (int)pointFloat[0]; 
    pointFloat[size++] = (float)spo_str; 
    pointFloat[0] = size; 
    //向前移动 
    if(size>=pointFloat.length){ 
     for(int i=2;i<pointFloat.length;i++){ 
      pointFloat[i-1] = pointFloat[i];  
     } 
     size--; 
     pointFloat[0] = size; 
    } 
    c++; 
 //由于数据量太大了,没10个点取一个更新到图表上 
    if(c%10==0){ 
     mVisualizerView.updateVisualizer(pointFloat); 
     c = 0; 
    }


效果图 :

 

 

 

posted on 2012-11-08 13:37  nuliniao  阅读(1444)  评论(0编辑  收藏  举报