Android: 利用SurfaceView绘制股票滑动直线解决延迟问题
1.背景介绍
最近项目要绘制股票走势图,并绘制能够跟随手指滑动的指示线(Indicator)来精确查看股票价格和日期。如下图所示:
上图中的那条白色直线就是股票的指示线,用来跟随手指精确确定股票的时间和股票价格。不论是绘制股票图还是绘制指示线,我们首先想到的就是用Android中的自定义View来实现。实践证明,使用View能够很好地实现静态的图片,但是对用动态图像的绘制,往往会出现延迟的现象。就如上图的指示线,实际用View类实现的,跟随手指移动时,指示线就会出现延迟的现象,严重影响了用户体验,这里自然而然的要用到SurfaceView以提高性能,提高滑动的流畅度。
2.Android中View和SurfaceView对比
下面例举了一下二者的区别:
View SurfaceView
只能在UI主线程中更新画面 UI主线程和新起的独立线程中都可更新画面
无双缓冲机制 采用双缓冲机制,速度快
还有其他区别希望大家补充。
3.案例
下面是该程序实现的主要代码,隐去了数据的填充。
package com.devin; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff.Mode; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; public class MyStockIndicatorView extends SurfaceView implements Callback { private SurfaceHolder surfaceHolder; private Paint paint; private float currentX; public MyStockIndicatorView(Context context) { super(context); // 初始化SurfaceHolder surfaceHolder = this.getHolder(); surfaceHolder.addCallback(this); // 让整个界面透明 surfaceHolder.setFormat(PixelFormat.TRANSPARENT); setZOrderOnTop(true); //初始化画笔 paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.WHITE); //设置界面可以点击 setFocusable(true); } //触屏事件,每次响应事件后改变坐标值,然后重新绘制 public boolean onTouchEvent(MotionEvent event) { int eventaction = event.getAction(); int x = (int) event.getX(); switch (eventaction) { case MotionEvent.ACTION_DOWN: currentX = x; paintIndicator(); break; case MotionEvent.ACTION_UP: clearCavas(); break; case MotionEvent.ACTION_MOVE: currentX; paintIndicator(); break; case MotionEvent.ACTION_CANCEL: clearCavas(); break; } return true; } //画直线 private void paintIndicator() { Canvas canvas = surfaceHolder.lockCanvas(); //下面两句用来改变原点、同时把默认的坐标系转换成笛卡尔坐标系 //canvas.translate(chartLeft, getHeight()); //canvas.scale(1, -1); canvas.drawLine(currentX, 0, currentX, getHeight(), paint); surfaceHolder.unlockCanvasAndPost(canvas); } //清屏 private void clearCavas() { //每次绘制前要锁定画布 Canvas canvas = surfaceHolder.lockCanvas(); canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); //绘制完成后解锁画布 surfaceHolder.unlockCanvasAndPost(canvas); } public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { } public void surfaceCreated(SurfaceHolder surfaceHolder) { //在此处初始化数据 initData(); } public void surfaceDestroyed(SurfaceHolder holder) { } }
上述代码关键地方都有注释。对于SurfaceView的实现,需要继承SurfaceView和实现Callback接口,同时需要实现三个方法:surfaceCreated、surfaceDestroyed、surfaceChanged方法,分别表示SurfaceView创建、销毁、界面改变时执行的方法。在构造函数中要初始化SurfaceHolder,同时每次绘图前要锁定画布,绘制完成后解锁画布。