当对视图数组调用 invalidate 方法时,第一个视图不会重绘
我有一个应用程序,其中使用适配器类在 3x3 GridView 中显示九个视图。在 GridView 单元格内包含的九个视图中,我使用 Canvas 和 Paint 对象来显示二维线图形;随后通过调用每个视图的 invalidate() 方法来修改并重新显示这些线条图形。
当在 Adapter 类的重写 getView() 方法中创建视图时,所有九个视图中的线条图形都会正确显示,但是当我尝试修改并随后重新显示线条图形时,所有视图都会成功刷新,除了 位于网格左上角的第一个视图,该视图继续显示原始线条图。我已经通过我的代码来确定第一个视图肯定是无效的,而且确实如此,所以我很困惑为什么在第一个视图上调用 invalidate() 方法不会导致它被重绘,而对所有剩余视图的相同调用会导致它们成功重绘。我还记录了对视图的 onDraw 方法的调用,这表明每次都会调用第一个视图的 onDraw 方法,因此我很确定这个问题不是由应用程序代码中的任何错误引起的。
修改和刷新九个视图的代码如下:
void updateViews(int parentTestCanvas) {
TestCanvasView testCanvas = testCanvass[parentTestCanvas];
double[] parentGenome = testCanvas.getGenome();
// Assign the parent genome to the testCanvass in the array
// The inherited genome will be subject to mutation in all cases except the first testCanvas
for(int testCanvasItem = 0; testCanvasItem < TestCanvasApp.geneCount; testCanvasItem++) {
testCanvas = testCanvass[testCanvasItem];
testCanvas.setGenome(parentGenome);
// Invalidate the testCanvas view to force it to be redrawn using the new genome
testCanvas.invalidate();
}
}
TestCanvasView 类中的 onDraw 方法如下:
protected void onDraw(Canvas canvas) {
float xOrigin = getMeasuredWidth() / 2;
float yOrigin = getMeasuredHeight() / 2;
canvas.drawPaint(cellPaint);
canvas.translate(xOrigin, yOrigin);
drawBranch(canvas, linePaint, 0, 0, this.length, this.direction, this.xInc, this.yInc, this.scale);
Log.d("TestCanvasView", "Drawing testCanvas " + mCellIndex);
}
private void drawBranch(Canvas canvas, Paint linePaint, double startX,
double startY, double branchLen, int branchDir, double[] xInc,
double[] yInc, double scale) {
branchDir = (branchDir + 8) % 8;
double newX = startX + branchLen * xInc[branchDir];
double newY = startY + branchLen * yInc[branchDir];
canvas.drawLine((float) (startX / scale), (float) (-startY / scale),
(float) (newX / scale), (float) (-newY / scale), linePaint);
if (branchLen > 1) {
drawBranch(canvas, linePaint, newX, newY, branchLen - 1, branchDir + 1, xInc, yInc, scale);
drawBranch(canvas, linePaint, newX, newY, branchLen - 1, branchDir - 1, xInc, yInc, scale);
}
}
有人知道为什么第一个视图没有被重绘吗?
I have an app in which I display nine views within a 3x3 GridView using an adapter class. Within each of the nine views that are contained within the cells of the GridView, I use Canvas and Paint objects to display a 2-dimensional line graphic; these line graphics are subsequently modified and re-displayed by invoking the invalidate() method of each view.
The line graphics in all nine views are displayed correctly when the views are created in the Adapter class’s overridden getView() method, but when I attempt to modify and then redisplay the line graphics subsequently, all of the views are refreshed successfully except for the first view in the top-left-hand corner of the grid, which continues to show the original line drawing. I’ve stepped through my code to establish that the first view is definitely being invalidated, and it is, so I’m baffled as to why the call to the invalidate() method on the first view doesn’t cause it to be redrawn, while the same call on all of the remaining views causes them to be redrawn successfully. I’ve also logged calls to the view’s onDraw method, and this shows that the first view’s onDraw method is called each time, so I’m pretty sure this problem is not being caused by any bugs in the application code.
The code that modifies and refreshes the nine views is as follows:
void updateViews(int parentTestCanvas) {
TestCanvasView testCanvas = testCanvass[parentTestCanvas];
double[] parentGenome = testCanvas.getGenome();
// Assign the parent genome to the testCanvass in the array
// The inherited genome will be subject to mutation in all cases except the first testCanvas
for(int testCanvasItem = 0; testCanvasItem < TestCanvasApp.geneCount; testCanvasItem++) {
testCanvas = testCanvass[testCanvasItem];
testCanvas.setGenome(parentGenome);
// Invalidate the testCanvas view to force it to be redrawn using the new genome
testCanvas.invalidate();
}
}
The onDraw method in the TestCanvasView class is as follows:
protected void onDraw(Canvas canvas) {
float xOrigin = getMeasuredWidth() / 2;
float yOrigin = getMeasuredHeight() / 2;
canvas.drawPaint(cellPaint);
canvas.translate(xOrigin, yOrigin);
drawBranch(canvas, linePaint, 0, 0, this.length, this.direction, this.xInc, this.yInc, this.scale);
Log.d("TestCanvasView", "Drawing testCanvas " + mCellIndex);
}
private void drawBranch(Canvas canvas, Paint linePaint, double startX,
double startY, double branchLen, int branchDir, double[] xInc,
double[] yInc, double scale) {
branchDir = (branchDir + 8) % 8;
double newX = startX + branchLen * xInc[branchDir];
double newY = startY + branchLen * yInc[branchDir];
canvas.drawLine((float) (startX / scale), (float) (-startY / scale),
(float) (newX / scale), (float) (-newY / scale), linePaint);
if (branchLen > 1) {
drawBranch(canvas, linePaint, newX, newY, branchLen - 1, branchDir + 1, xInc, yInc, scale);
drawBranch(canvas, linePaint, newX, newY, branchLen - 1, branchDir - 1, xInc, yInc, scale);
}
}
Anyone got any ideas as to why the first view is not being redrawn?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好的,终于找到了问题的根源 - 事实证明,TestCanvasAdapter 类中的 getView 方法对于 testCanvass[0] 被调用了两次,但对于所有其他元素只调用了一次。我天真地假设 Adapter 类的 getView 方法将为数组中的每个元素调用一次,但是 这篇文章证实 getView 可能会因为各种晦涩的原因被多次调用,而这些原因对于像我这样缺乏经验的 Android 开发人员来说不太可能显而易见。一旦理解了这一点,我就可以轻松添加下面的逻辑来检查 testCanvass[position] 是否不为 null,然后再在 TestCanvasAdapter.getView 方法中将视图引用分配给它,从而解决了问题。
非常感谢 Romain Guy 不厌其烦地回答这个问题。
OK, finally got to the bottom of this - it turns out that the getView method in the TestCanvasAdapter class was called twice for testCanvass[0], but only once for all the other elements. I had naively assumed that the Adapter class's getView method would be called exactly once for each element in the array, but this post confirms that getView may be called more than once for various obscure reasons that are unlikely to be readily apparent to inexperienced Android developers like me. Once I understood this, I was easily able to add the logic below to check that testCanvass[position] was not null before assigning the view reference to it within the TestCanvasAdapter.getView method, which resolved the problem.
Many thanks to Romain Guy for taking the trouble to reply to this query.
如果调用第一个视图的 onDraw() 方法,则 invalidate 起作用。如果视图不与剪切矩形相交,则不会调用 onDraw()。您可以通过打开开发工具应用程序(在模拟器上,但也可以将其安装在手机上)并在开发设置屏幕中打开“显示屏幕更新”来验证这一点。它将闪烁屏幕上无效/重绘的区域。
If the first view's onDraw() method is invoked, then the invalidate worked. onDraw() is not called if the View doesn't intersect with the clipping rectangle. You can verify this by opening the Dev Tools app (on the emulator but you can also install it on a phone) and in the development settings screen, turn on "Show screen updates." It will flash regions of the screen that get invalidated/redrawn.