我应该将SurfaceView或View用于经常更新的视图(15fps)

时间:2013-09-26 09:43:38

标签: android android-custom-view

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

到目前为止,我一直在使用自定义视图,扩展View类。我在包含片段中有一个线程,每隔~70ms重写一次视图就会调用视图上的invalidate()。这本身并没有引起问题,因为我已经优化了我的onDraw()函数,大部分时间都在2ms以内运行。

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

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

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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();也许这是正确的我没有调查它。