我是Android开发新手并阅读了本书Hello Android
。它使用Sudoku示例,我所指的代码是here.
在这个onTouchScreen中,它调用select方法,调用invalidate两次。问题是,在invalidating
之后是onDraw
方法之后调用了吗?在这种情况下,在我的select方法中,它将执行
它是如何发生的,整个屏幕也会被重新生成吗?所有的数字和提示等,因为从作者所说的书
在此示例的早期版本中,我使整个版本无效 移动光标时屏幕。因此,在每一把钥匙上 按下,整个拼图必须重新绘制。这导致它滞后 显着。切换代码仅使最小的无效 改变的矩形使它运行得更快。
他究竟想在这说什么?
已添加信息
我在onDraw方法中添加了一些日志,一些在开始时添加,一些在for循环中。每当我触摸一个新的矩形时,都会调用所有日志。这是否意味着整个屏幕正在重新填充,因为onDraw中的所有代码都被重新执行?
答案 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
。