我是否在每次通话时使整个屏幕无效?

时间:2014-01-09 17:32:11

标签: android optimization ondraw invalidation

我是Android开发新手并阅读了本书Hello Android。它使用Sudoku示例,我所指的代码是here.

在这个onTouchScreen中,它调用select方法,调用invalidate两次。问题是,在invalidating之后是onDraw方法之后调用了吗?在这种情况下,在我的select方法中,它将执行

  1. 无效
  2. 调用onDraw
  3. 做一些事情
  4. 无效
  5. 调用onDraw
  6. 它是如何发生的,整个屏幕也会被重新生成吗?所有的数字和提示等,因为从作者所说的书

      

    在此示例的早期版本中,我使整个版本无效   移动光标时屏幕。因此,在每一把钥匙上   按下,整个拼图必须重新绘制。这导致它滞后   显着。切换代码仅使最小的无效   改变的矩形使它运行得更快。

    他究竟想在这说什么?

    已添加信息

    我在onDraw方法中添加了一些日志,一些在开始时添加,一些在for循环中。每当我触摸一个新的矩形时,都会调用所有日志。这是否意味着整个屏幕正在重新填充,因为onDraw中的所有代码都被重新执行?

6 个答案:

答案 0 :(得分:5)

Kraken

问:但是日志呢,当然,如果我的循环被执行,这意味着所有的canvas.draw也会被执行? 答:是的,整个图纸将在您的示例代码中执行。您必须自己在onDraw方法中优化渲染过程。

问:系统如何知道,哪一段代码“只”重绘脏区?
答:Canvas::getClipBounds会给你一个脏的矩形,你应该画一些东西 在for loop onDraw内,将脏矩形与您想要绘制的矩形进行比较。如果它们不相交,则执行continue

但请记住,如果您将多个区域设置为脏,则返回的rect将是所有脏区域的并集 请参阅以下两个问题:
Getting the dirty region inside draw()
Android: invalidate(dirty)

希望这会对你有所帮助。

==========================

作者是对的。但这仍然可以优化。

调用invalidate(Rect)将自动为画布设置剪辑区域。 (这就是canvas.getClipBounds()可以返回该区域的原因。) 然后,在onDraw()期间,将忽略从剪辑区域中抽出的任何内容。它们不会出现在屏幕上,因此真的会缩短绘图时间 但忽略它们仍然需要花费很多成本。因此,对于图形密集型应用,如果您提前排除onDraw(),则可以更好地优化onDraw()

你可以找到一个很好的例子来优化android KeyboardView中的{{1}},它提供了你的android输入法的视图。 http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/inputmethodservice/KeyboardView.java

答案 1 :(得分:2)

这直接来自View documentation

  

通过遍历树并渲染与无效区域相交的每个视图来处理绘图。因为树是按顺序遍历的,这意味着父母将在他们的孩子之前(即后面)绘制,兄弟姐妹按照他们出现在树中的顺序绘制。如果为View设置了一个可绘制的背景,那么View会在回调它的onDraw()方法之前为你绘制它。

     

请注意,框架不会绘制不在无效区域中的视图。

根据我的理解,一旦你的视图第一次被绘制,一个树如果形成了父对象和子对象以及它们在屏幕上的位置。当您将指定区域传递给无效时,将检查该树中该区域中受影响的节点,并且仅调用那些节点进行绘制。

现在我也不明白的是,在这个例子中,唯一的View是PuzzleView。我不确定如何优化单个视图的绘图。检查是否在文中进一步讨论。

如果不是,那么我的理论是画布对象(矩形)也是上述树的一部分,只有那些部分,即指定区域中的矩形被绘制。

更重要的是,在使用区域无效与完全无效后,您是否看到任何改进?

答案 2 :(得分:1)

即使多次调用invalidate,onDraw方法也只会被调用一次。基本上,当视图失效时,onLraw会在RunLoop方法内调用。这意味着如果在将控件返回到runloop之前多次使视图无效,则视图将仅重绘一次。 请注意,如果您使视图的两个不同的部分无效,系统将在重新绘制视图之前尝试将这些部分组合在一起。

答案 3 :(得分:0)

在代码中,您正在谈论的无效是:

  invalidate(selRect);

如果是,他只调用所选矩形selRect的onDraw。

invalidate();重绘孔屏幕。

希望它有所帮助。

答案 4 :(得分:0)

在此示例中,您应该注意到invalidate()次调用的参数为Rect。这意味着只有视图的这个区域变脏并且将由系统重新绘制。

调用invalidate()不会在之后触发onDraw()方法。系统只会决定何时重绘视图。

来自Android文档:

  

如果视图可见,将调用onDraw(android.graphics.Canvas)   在未来的某个时刻。

知道在select方法中,这可能会发生: 1.使视图的一小部分无效 做一些事情 3.使视图的另一小部分无效 4.视图的这两部分正在重新绘制

希望有所帮助。

答案 5 :(得分:0)

正如上面提到的@jjxtra

<块引用>

Invalidate with rect 不会在硬件加速打开的情况下改变画布剪辑边界。无论传递给 Invalidate 的矩形如何,都会始终重绘整个视图。

在 API 21 中,给定的矩形被完全忽略,取而代之的是内部计算的区域。 public void invalidate(int l, int t, int r, int b);public void invalidate(Rect dirty); 已标记为已弃用!

我通过指定要绘制的位图子集解决了这个问题。

class MyView extends View {
    private Bitmap mBitmap;
    private Rect mBound = new Rect(0, 0, 300, 300); // 300x300 by default, invoke updateBound if in needed
    
    ...
    
    private void updateBound(PointF pointF) {
        if (mBound.left > (int)pointF.x) {
            mBound.left = (int)pointF.x;
        }
        if (mBound.bottom < (int)pointF.y) {
            mBound.bottom = (int)pointF.y;
        }
        if (mBound.top > (int)pointF.y) {
            mBound.top = (int)pointF.y;
        }
        if (mBound.right < (int)pointF.x) {
            mBound.right = (int)pointF.x;
        }
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        ...
        invalidate();
        return true;
    }

@Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(mBitmap, mBound, mBound, null);
    }
}

默认情况下,我只在 (0, 0, 300, 300) 的区域内绘制。但是您可以根据需要更新边界,只需调用 updateBound