android---surfaceview小结--(1)

在最近一个项目中需要在android端做曲线图并且高频刷新,所以来总结一下surfaceview的使用方法!
首先,项目要求是app通过网络接收数据,同时在屏幕上通过曲线图显示出来。数据更新的频率大约是每秒绘制120个点左右,而android系统设计的是绘图每秒刷新60次,那么每一次绘图大约绘制2个点,做出两段曲线
那么现在设想是两个线程,一个线程模拟从网络接收数据,一个线程用来绘图。那么步骤如下:
1. 继承surfaceview,实现SurfaceHolder.Callback接口:
public class TicSurfaceView extends SurfaceView implements SurfaceHolder.Callback
2. 重写如下方法,都是callback接口的:
(1)public abstract void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
在这里设置对突发事件的处理。
(2)public abstract void surfaceCreated (SurfaceHolder holder)
在这里开启绘图线程。在本项目中开启socket线程用来接收数据。
(3)public abstract void surfaceDestroyed (SurfaceHolder holder)
用来终止绘图线程。
3. 部分代码分析
(1)
public void surfaceCreated(SurfaceHolder holder){
isNet = true; //线程开启,标志正在从网络上获取资源。
drawThead = new DrawThead(); //绘图线程
netThreadTest = new NetThreadTest(); //测试时模拟联网接受数据
drawThead.start();
netThreadTest.start();
}

在绘制过程中,安全的开启和终止一个线程最好是设置一个flag,线程开启时flag为true,在绘图的每一个循环当中检查flag,如果为false,那么就终止循环,终止线程。
(2)
public void surfaceDestroyed(SurfaceHolder holder) {
isNet = false;
drawThead = null;
netThreadTest = null;
//netThread = null;
}

在这里终止绘图以及网络线程。在此记录一下犯的一个错误:

public TicSurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        initLinePaint();
        //错误:
        drawThead = new DrawThead();
        netThreadTest = new NetThreadTest();
    }
 public void surfaceCreated(SurfaceHolder holder){
        isNet = true; //线程开启,标志正在从网络上获取资源。
        drawThead.start();
        netThreadTest.start();
    }
public void surfaceDestroyed(SurfaceHolder holder) {
        isNet = false;
    }

在运行时,返回主屏幕,再返回应用,会:

java.lang.IllegalThreadStateException: Thread already started

此时,线程的run()方法已经运行完毕,activity中可能仍存在着该线程的实例对象,但是它已经不能再视为:独立可执行的了,线程的独立的call stack已经被dissolved,线程已经进行dead状态。不能再次start。
(3)

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        xOld = 0;
        yOld = ryCenter;
        Log.d(TAG_S,"Changed!!");
    }

在这里只需设置重新开始绘图即可。
(4)绘图线程:

class DrawThead extends Thread{
       @Override
       public void run() {
           super.run();
           ArrayList<int[]> drawbuf = new ArrayList<>(); //用来缓存数据
           boolean flag =true;
           while (isNet){ //当处于联网状态时
               synchronized (data){ //data是临界缓冲区
                   if (data.size() == 0){   
                       flag = false;
                   }else {
                       flag = true;
                       drawbuf = (ArrayList<int[]>)data.clone();//先取出数据,再绘制,避免网络线程等待
                       data.clear();//清空数据区
                   }
               }
               if (flag){
                   for (int i=0;i<drawbuf.size();i++){
                       int[] drawEachBuf = drawbuf.get(i);
                       simpleDraw(drawEachBuf);
                   }
               }

           }
       }
   }
private void simpleDraw(int[] dataone) {
        int xNew;
        int yNew;
        int yNew2;
        for (int i = 0; (i +1)< dataone.length && isNet; i=i+2) {
            if (xOld+2*defaultXoffset > mWidth){ //超出屏幕范围,重新绘制
                Canvas canvasT = surfaceHolder.lockCanvas(new Rect(xOld,0,mWidth,mHeight));
                canvasT.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);
                surfaceHolder.unlockCanvasAndPost(canvasT);
                xOld = 0;
                yOld = ryCenter;
            }
            xNew = xOld + 2*defaultXoffset;
            Canvas canvas = surfaceHolder.lockCanvas(new Rect(xOld, 0, xNew, mHeight));//在这里,锁定的canvas范围应当为绘图所需要的范围,不能太大,否则会因为刷新效率问题导致残影。
            canvas.drawColor(Color.TRANSPARENT,PorterDuff.Mode.CLEAR);//清除画布
            yNew = ryCenter + dataone[i] * yScale;
            yNew2 = ryCenter + dataone[i+1]*yScale;
            canvas.drawLine(xOld, yOld, xNew-defaultXoffset, yNew, linePaint);
            canvas.drawLine(xNew-defaultXoffset,yNew,xNew,yNew2,linePaint);
            xOld = xNew;
            yOld = yNew2;
            surfaceHolder.unlockCanvasAndPost(canvas);
        }
    }

绘图过程:首先获取Canvas,锁定,然后清除画布,画线,再提交释放。
再记录一下遇到的残影问题:设置绘图策略为:先清理整个屏幕,再绘制每一个点,绘完屏幕后再次清屏。这会导致在屏幕的左端出现范围不定的残影,但是如果自己手动测试会发现每一次的清屏仍然是有效的,即整个view被绘制为黑色,但是再次绘点描线时会出现残影,而且仍旧是在view的起始绘制处。
先写到这里,接下来的surfaceview的刷新频率,以及有关的paint和color相关的东西放到下一篇。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值