0

我正在构建一个需要实时显示信号轨迹的应用程序(想想你在医院的心脏监视器上看到的那种)。为了使我的线条动画看起来流畅,我希望帧速率约为每秒 15 帧(我做了一些实验得出结论,这是可接受的最低帧速率)。在这篇文章的上下文中可能无关紧要,但我可能在 ListView 中有很多这样的视图(大约 20 个很常见,每次显示大约 5 个)。

到目前为止,我一直在使用自定义视图来执行此操作,扩展 View 类。我在包含片段中有一个线程,它每隔约 70 毫秒在视图上调用 invalidate() 来重绘视图。这本身不会引起问题,因为我已经优化了我的 onDraw() 函数,大多数时间都在 2 毫秒内运行。

现在我在片段中添加了一个 Spinner 并且在调试它时我注意到一旦我打开 Spinner 适配器不断地点击 getView() 调用,即使我没有触摸 Spinner(即打开但不滚动)并且也滞后少量。这让我意识到我的整个片段每隔约 70 毫秒就会被重绘一次,这对我来说听起来很糟糕。现在的问题:

  • 有没有办法在子视图上触发 onDraw() 而不会导致整个层次结构的重绘?
  • 我应该求助于 SurfaceView 吗?(这不会导致完整的视图层次结构重绘,对吧?)
    • 我注意到 SurfaceView 没有硬件加速。如果我只使用基本的绘图函数(drawLine() 和 drawText()),这有关系吗?
    • 在我的情况下 GLSurfaceView 会更好吗?
4

1 回答 1

0

我将尝试回答我的问题,以便它可能对可能遇到相同问题的其他人有用。

在 ListView 中包含一堆 SurfaceView 是失败的。在我看来,因为 SurfaceView 绘图未与 UI 的其余部分同步,所以当您滚动时,您会在视图之间看到黑线闪烁(这可能是因为 SurfaceView 通过 getView() 重新分配给新数据源并在它获取之前显示有机会重绘自己)。显然,在 getView() 中触发重绘是不够的。

Delyan 对我的问题的第一条评论是有效的,尽管它可能并不总是适用。如果我手动浏览 ListView 中的每个视图并在其上调用 invalidate() 重绘将不会通过层次结构冒泡(我不需要 invalidate(l,t,r,b) 签名,但它可能是一个聪明的如果您遇到过度重绘的问题,请尝试一下)。因此,尽管 Romain Guy 在他的演讲中提到 invalidate() 会冒泡到根,但显然情况并非总是如此。Delyan 关于硬件/软件的不同实现的评论可能是原因 - 我在硬件中呈现我的观点。

无论如何,为了解决这个问题,我在适配器中创建了这个方法:

public void redrawTraces(ListView lv) {
    final int viewCount = lv.getChildCount();
    for(int i=0; i < viewCount; i++) {
        final View stv = lv.getChildAt(i);
        if(stv != null) {
            stv.invalidate();
        }
    }
}

视图不是 SurfaceViews 的地方。最后,为了弄清楚我做错了什么,我这样做了:

adapter.notifyDataSetChanged();

这个调用确实冒泡并导致整个层次结构重绘。我的假设是,就更新整个层次结构而言,这大致相当于调用 listView.invalidate() ;也许这是正确的,我没有调查它。

于 2013-09-26T14:34:06.273 回答