在 SurfaceView 上绘制对象的轨迹
Draw object's trails on a SurfaceView
我必须模拟一些物体的运动,所以我创建了一个 SurfaceView,我在上面用专用线程绘制它们。我调用 canvas.drawColor() 的每个循环来清除所有先前对象的位置并绘制新状态。一切正常,帧率也不错。
问题是:如果我想绘制物体运动轨迹怎么办?在那种情况下,我必须记住每个对象的位置,并在每个循环中绘制所有过去的位置,即数百个点。这个任务保持较低的帧速率,在我看来唯一的方法是每次都重绘相同的点是荒谬的!有一种方法可以保持 canvas 上绘制的点,而不是在每个循环中用 canvas.drawColor() 取消它们(这对于其他任务是必需的)?
有点。
SurfaceView 的 Surface 使用了多个缓冲区。如果它是双缓冲的,并且您不是每帧都清除屏幕,那么您将在一个缓冲区中渲染所有奇数帧,在另一个缓冲区中渲染所有偶数帧。每次你画一个新的框,它会翻转到另一个缓冲区,你的一半位置会消失(看起来一切都在振动)。
您可以在每一帧上,在每个对象的当前位置和之前的位置绘制每个对象。这样两个框架都会得到每个对象的位置。
这个想法的实际问题是您不知道 Surface 使用了多少缓冲区。如果它是三重缓冲的(这是很有可能的),那么您将需要绘制当前、先前和先前-先前的位置以确保每个缓冲区都有每个位置。更多数量的缓冲区在理论上是可能的,但不太可能。
说了这么多,您不想采用这种方法的原因很简单:当您锁定 canvas 时,您同意修改脏区中的每个像素。如果不这样做,结果将不可预测,并且您的应用程序可能会在操作系统的未来版本中异常崩溃。
做你想做的最好的方法是在屏幕外的位图上绘制,然后将整个东西 blit 到 Surface 上。一开始这是一个巨大的浪费,因为您只是为几个对象复制了一个屏幕大小的位图,但很快减少的绘制调用就会开始取胜。
创建一个与 Surface 大小相同的位图,然后使用 constructor that takes a Bitmap 创建一个 Canvas。通过此 Canvas 完成所有绘图。当你想更新屏幕时,在 SurfaceView 的 Canvas.
上使用 drawBitmap()
方法
由于性能成本,我建议不要使用软件缩放 -- 确保您正在执行 1:1 副本。您可以使用 setFixedSize()
call on the SurfaceView surface to make it a specific size if that's helpful -- for devices with larger pixel densities it can improve your frame rates and reduce battery usage (blog post here).
我必须模拟一些物体的运动,所以我创建了一个 SurfaceView,我在上面用专用线程绘制它们。我调用 canvas.drawColor() 的每个循环来清除所有先前对象的位置并绘制新状态。一切正常,帧率也不错。
问题是:如果我想绘制物体运动轨迹怎么办?在那种情况下,我必须记住每个对象的位置,并在每个循环中绘制所有过去的位置,即数百个点。这个任务保持较低的帧速率,在我看来唯一的方法是每次都重绘相同的点是荒谬的!有一种方法可以保持 canvas 上绘制的点,而不是在每个循环中用 canvas.drawColor() 取消它们(这对于其他任务是必需的)?
有点。
SurfaceView 的 Surface 使用了多个缓冲区。如果它是双缓冲的,并且您不是每帧都清除屏幕,那么您将在一个缓冲区中渲染所有奇数帧,在另一个缓冲区中渲染所有偶数帧。每次你画一个新的框,它会翻转到另一个缓冲区,你的一半位置会消失(看起来一切都在振动)。
您可以在每一帧上,在每个对象的当前位置和之前的位置绘制每个对象。这样两个框架都会得到每个对象的位置。
这个想法的实际问题是您不知道 Surface 使用了多少缓冲区。如果它是三重缓冲的(这是很有可能的),那么您将需要绘制当前、先前和先前-先前的位置以确保每个缓冲区都有每个位置。更多数量的缓冲区在理论上是可能的,但不太可能。
说了这么多,您不想采用这种方法的原因很简单:当您锁定 canvas 时,您同意修改脏区中的每个像素。如果不这样做,结果将不可预测,并且您的应用程序可能会在操作系统的未来版本中异常崩溃。
做你想做的最好的方法是在屏幕外的位图上绘制,然后将整个东西 blit 到 Surface 上。一开始这是一个巨大的浪费,因为您只是为几个对象复制了一个屏幕大小的位图,但很快减少的绘制调用就会开始取胜。
创建一个与 Surface 大小相同的位图,然后使用 constructor that takes a Bitmap 创建一个 Canvas。通过此 Canvas 完成所有绘图。当你想更新屏幕时,在 SurfaceView 的 Canvas.
上使用drawBitmap()
方法
由于性能成本,我建议不要使用软件缩放 -- 确保您正在执行 1:1 副本。您可以使用 setFixedSize()
call on the SurfaceView surface to make it a specific size if that's helpful -- for devices with larger pixel densities it can improve your frame rates and reduce battery usage (blog post here).