Android SurfaceView仅绘制两次:如何让onDraw方法持续调用?
解决SurfaceView仅渲染两次的问题
嘿,我来帮你搞定这个SurfaceView的绘图问题!首先得明确一点:SurfaceView的onDraw()方法本来就不是用来实现持续渲染的,这也是你设置setWillNotDraw(false)后依然只触发两次的核心原因。
为什么你的代码会只渲染两次?
SurfaceView的设计和普通View完全不同:它的绘图区域是一个独立的Surface,和UI线程的View层级是分离的。onDraw()在这里只是用来绘制Surface上方的View内容(比如一些叠加层),而不是更新Surface本身的画面。默认情况下,SurfaceView的onDraw()只会在View初始化和布局变化时被调用,所以你看到的两次渲染就是这个原因。
正确的持续渲染方式:用SurfaceHolder+子线程循环绘图
要实现持续调用绘图逻辑,你需要利用SurfaceHolder的API,在子线程中循环获取Canvas、绘制内容、提交画布。具体步骤如下:
- 定义一个绘图线程类,负责循环绘制
- 在
surfaceCreated()回调中启动线程 - 在
surfaceDestroyed()回调中停止线程,避免内存泄漏 - 在线程中通过
lockCanvas()获取画布,绘制完成后用unlockCanvasAndPost()提交
修改后的完整代码示例
public class MainActivity extends Activity implements SurfaceHolder.Callback { private static final String TAG = "SurfaceView"; private SurfaceView surfaceView; private SurfaceHolder surfaceHolder; private DrawThread drawThread; private boolean isDrawing = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = findViewById(R.id.surface_view); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); } @Override public void surfaceCreated(SurfaceHolder holder) { // 启动绘图线程 isDrawing = true; drawThread = new DrawThread(); drawThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // 当Surface尺寸变化时,可以在这里调整绘图参数 } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 停止线程,避免内存泄漏 isDrawing = false; try { drawThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } // 自定义绘图线程 private class DrawThread extends Thread { @Override public void run() { super.run(); while (isDrawing) { Canvas canvas = null; try { // 锁定画布,获取绘图权限 canvas = surfaceHolder.lockCanvas(); if (canvas != null) { // 在这里写你的绘图逻辑,比如绘制图形、文字等 drawContent(canvas); } } catch (Exception e) { e.printStackTrace(); } finally { // 必须释放画布,否则会导致Surface卡顿或崩溃 if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } // 控制帧率,比如每秒30帧 try { Thread.sleep(33); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 你的绘图逻辑方法 private void drawContent(Canvas canvas) { // 示例:绘制背景和一个移动的矩形 canvas.drawColor(Color.WHITE); long currentTime = System.currentTimeMillis(); float x = (currentTime % 1000) / 1000f * surfaceView.getWidth(); canvas.drawRect(x, 100, x + 100, 200, new Paint(Color.RED)); } }
关键注意事项
- 必须在finally块中释放画布:如果不调用
unlockCanvasAndPost(),Surface会被一直锁定,导致无法继续绘图甚至ANR。 - 控制帧率:添加
Thread.sleep()避免CPU占用过高,根据需求调整睡眠时间(比如33ms对应约30帧,16ms对应约60帧)。 - 线程安全:确保
isDrawing变量的可见性,或者用volatile修饰,避免线程间的状态不一致。
内容的提问来源于stack exchange,提问作者likaa




