【问题标题】:When invalidate method called on an array of Views, the first View is not redrawn在视图数组上调用 invalidate 方法时,第一个视图不会重绘
【发布时间】:2011-01-21 15:41:59
【问题描述】:

我有一个应用程序,我使用适配器类在 3x3 GridView 中显示九个视图。在 GridView 的单元格中包含的九个视图中的每一个中,我使用 Canvas 和 Paint 对象来显示二维线图形;这些线形图形随后通过调用每个视图的 invalidate() 方法进行修改和重新显示。

在Adapter类的重写getView()方法中创建视图时,所有九个视图中的线图都正确显示,但是当我尝试修改并随后重新显示线图时,所有视图都刷新成功except 用于网格左上角的 first 视图,该视图继续显示原始线条图。我已经通过我的代码来确定第一个视图肯定是无效的,而且确实如此,所以我很困惑为什么在第一个视图上调用 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);
    }

}

有人知道为什么第一个视图没有被重绘吗?

【问题讨论】:

    标签: android view invalidation


    【解决方案1】:

    好的,终于明白了——原来TestCanvasAdapter 类中的getView 方法为testCanvass[0] 调用了两次,但对所有其他元素只调用了一次。我天真地假设 Adapter 类的 getView 方法会为数组中的每个元素只调用一次,但this post 确认 getView 可能会因为各种晦涩的原因被多次调用,这些原因对于没有经验的 Android 开发人员来说不太明显像我这样的。一旦我理解了这一点,我就可以轻松地添加下面的逻辑来检查 testCanvass[position] 是否不为空,然后在 TestCanvasAdapter.getView 方法中将视图引用分配给它,从而解决了问题。

                // Add the newly created TestCanvasView object to the array on the TestCanvasApp object
            if (position >= 0 && position < TestCanvasApp.viewCount 
                    && mTestCanvasApp.testCanvass[position] == null) {
                        mTestCanvasApp.testCanvass[position] = testCanvasView;
            }
    

    非常感谢 Romain Guy 不厌其烦地回复此问题。

    【讨论】:

    • 另一个微妙之处是对位置 0 的多次调用不一定会回收同一个视图。这会使有状态列表项的缓存逻辑复杂化。
    【解决方案2】:

    如果调用了第一个视图的 onDraw() 方法,则无效操作有效。如果 View 不与剪切矩形相交,则不会调用 onDraw()。您可以通过打开 Dev Tools 应用程序(在模拟器上,但您也可以将其安装在手机上)并在开发设置屏幕中打开“显示屏幕更新”来验证这一点。它会闪烁屏幕上无效/重绘的区域。

    【讨论】:

    • 好的,我已经确定对 invalidate() 的调用导致所有视图都被重绘,包括视图 0。问题的真正原因似乎是对 testCanvass 所做的更改updateViews 内的[0] 没有“粘”,即testCanvass[0] 似乎在updateViews 范围内修改成功,但是一旦updateViews 方法执行完成并结束,testCanvass[0] 的原始内容就神秘地恢复了,与testCanvass[0]的原始绘图说明,所以它的线条图形在屏幕上没有变化。
    • 这只发生在 testCanvass[0]; testCanvass[] 数组中的所有其他视图在 updateViews 完成后保持修改。我不得不说我对这种行为完全感到困惑。 Java(或 Java 的 Android 实现)是否会以与其他元素不同的特殊方式处理对数组第 0 个元素的引用??
    • 不,第 0 个元素的处理方式与其他元素一样。
    猜你喜欢
    • 2011-04-08
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-23
    • 2012-05-25
    • 1970-01-01
    相关资源
    最近更新 更多