Android:有效地滚动画布的内容?

Posted

技术标签:

【中文标题】Android:有效地滚动画布的内容?【英文标题】:Android: Efficiently scrolling the contents of a Canvas? 【发布时间】:2010-10-30 03:30:58 【问题描述】:

我想绘制一个实时更新的图表(从右侧开始)。我能想到的最有效的方法是将 x[0 .. width-2] 中的所有内容复制 1 个像素,然后在 x[width-1] 处绘制新值。

我对 android 几乎没有经验,但据我所知,Canvas 根本无法操作它的内容。我每次都需要重新绘制整个屏幕吗?这涉及缩放和平滑,所以我担心它会很慢。

我应该画成一个 byte[][] 然后用它来绘制屏幕(每次都将缓冲区的内容向左移动)?

【问题讨论】:

我认为我看到了一种使用 Bitmap、getPixels 和 setPixels 的可能方法。不确定这是否是一种不好的方法,但我正在试一试。请指教! 【参考方案1】:

如果您的图表是有界的,请尝试将其全部渲染一次到一个图像,然后将该图像中的相关部分blit到您的画布上。尽量避免实际“移动”缓冲区中的像素,因为这可能会在您的读取和写入之间引入依赖关系,并且可能会真正降低性能。实际上,从一个缓冲区复制到另一个缓冲区并交替将哪个缓冲区传送到屏幕上可能会更好。最后,如果您最终不得不手动处理像素,请确保您在图像上运行的是行而不是列,并且从行的开头开始以帮助缓存。

【讨论】:

【参考方案2】:

关于性能,没有分析我们不能说。

可能是您的目标手机上的线条绘制是硬件加速的,您应该在每一帧都使用线条绘制图元从头开始绘制图形。

另一方面,图像缓冲区的直接像素操作是:

创建一个大小合适的图像并将其清除为“background_color”。此图像需要具有 setpixel() 功能。

有一个值数组记录每次 x 的 y,因此对于任何列,您都知道上次绘制图表的位置。

将此“chart_image”和“chart_array”视为循环缓冲区。对于每个时间步:

Y = ...;
X = time_since_start % chart_width;
chart_image.setpixel(X,chart_array[X],background_color); // clear previous line
chart_array[X] = Y;
chart_image.setpixel(X,chart_array[X],foreground_color); // draw new plot

现在你需要对它进行 blit。您需要对图像进行两次 blit:

X = time_since_start % chart_width;
// the newest data is on the left of the chart_image but gets drawn on the right side of the output
blit(out_x+X,out_y, // destination coordinates
    chart_image,
    0,0, // top left of part of chart_image to blit
    X,chart_height); // bottom right of chart_image part
// the oldest data is on the right of the chart_image but gets drawn on the left side of the output
blit(out_x,out_y,
    chart_image,
    X,0,
    chart_width,chart_height);

如果您想使用线条而不是单个像素,事情会变得更加棘手,但是 drawline() 而不是 setpixel() 也可以使用这种方法。

(很抱歉不知道 Android API;但方法是通用的。)

【讨论】:

【参考方案3】:

只是一个想法,您可能已经考虑过,但我不会改变缓冲区的内容 - 我只是尝试像循环缓冲区一样使用它。保留当前列的索引,一旦您再次绕到最左侧的列,您可以分两段绘制到目标 - 当前列右侧的内容和左侧的内容,包括最近填充的列。这样您就不必四处移动任何东西,并且每次屏幕刷新只是两个段的两个 blit(位图副本)。如果那一点太慢,您仍然可以始终将它们绘制到第二个屏幕外缓冲区中,然后一次将整个内容传输到屏幕上。当然,屏幕上的一大块 blit 相当快,不是吗?

【讨论】:

另外,我忘了提到一些事情(你可能认为这很明显,但我不知道你知道什么,所以请原谅我) - 我会确保你的缓冲图像是/被初始化以匹配您希望绘制的屏幕区域的大小,并将您所做的图形绘制的一点点缩放到那些,这样当您执行大块时,源和目标尽可能匹配(相同高度和位图格式),不需要缩放。【参考方案4】:

既然我认为您将图形数据存储在内存中是理所当然的,因此重绘它应该不是问题。每帧重绘一组点一点也不费力。移动内存将是密集的,它移动所有东西而不是只绘制你需要的东西。 最坏的情况,因为它是时间的函数,显示器的每列只有一个值,系统必须绘制的横向大约 800 像素/值。这是微不足道的。 你有没有介绍过这个? 编辑:记住,不是系统必须绘制每个点,它只绘制内存,然后使用它的原语。不要认为它会重复绘制点、转储到视频内存,然后再重复一次。

【讨论】:

以上是关于Android:有效地滚动画布的内容?的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中平滑滚动画布

Android Chrome 上的 HTML 画布不滚动窗口

为啥Android系统一次又一次将相同的内容绘制到画布上

画布无限滚动映射对象位置

Android 自定义视图和画布大小

安卓画布滚动